I’m currently making revisions to a product designer I released in November. One of the revisions is to categorize a theme selector. Once I received the new comps from the client, I noticed they snuck in a combo box menu into their revised design. Not your ordinary flash component combo box either, but there very own stylized drop down menu.
So, I started out by searching Google and looking for something I could easily implement or re-engineer, but to no avail. People were writing stinky code..using callbacks, adding tweening engines,ect.or making it really complex. I just needed a simple comb box. Well, I got impatient and wrote it myself. I quickly scripted up the comb box the client requested, but I thought, what the hell, I’ll go the extra mile and make it really flexible and add it to the good ole blog. Maybe it well help some designer/coder out on the world wide web.
Here it is:
Before we go over the code I should explain why I decided to make this a MovieClip that lives in the library. First off, my clients comp has a cool little vector background for the default state and a triangle button. Knowing clients and there desire to change design on a whim I decided to go the MovieClip route instead of using the drawing API. Also, when I was doing my research I noticed for most of the code snippets being offered, designers where asking how they could change the colors or design of the combo box. So, I decided to give you all that flexibility.
Lets start with the good ole document class Main.as.
Main.as
/****************************
* Manuel Gonzalez *
* design@stheory.com *
* www.stheory.com *
* www.codingcolor.com *
*****************************/
package{
import flash.display.*;
import com.dropdown.DropDown;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.text.TextFormat;
import flash.text.Font;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
import fl.controls.Button;
public class Main extends Sprite{
private var dropDwn:MovieClip;
private var dropDwn2:MovieClip;
private var t:TextField;
private var dropDArray:Array = new Array({label:"Hip & Stylish", data:0},{label:"Christmas", data:1},{label:"Easter", data:2},{label:"Slick", data:3});
private var dropDArray2:Array = new Array({label:"Hip & Stylish", data:0},{label:"Christmas", data:1},{label:"Easter", data:2},{label:"Slick", data:3});
private var dropDArray3:Array = new Array({label:"Test 1", data:0},{label:"Test2", data:1},{label:"Test 3", data:2},{label:"Test 4", data:3});
private var currArray:int = 0;
public function Main(){
dropDwn = new DropDown();
dropDwn.x = 25
dropDwn.y = 25;
dropDwn.init(dropDArray,150, 25);
/*
use an embeded font
var helve:Font = new HelveBold();
dropDwn.customFont = helve;
*/
/*change the font size
dropDwn.fontSize = 9;
*/
/*
change the font
dropDwn.fontName = "Times";
*/
/*
change the font default color & over color
dropDwn.itemFontColors = new Array(0xff0000,0x00ff00);
*/
/*
change the item background default color & over color
dropDwn.itemColors = new Array(0xff0000,0x00ffff);
*/
/*
change the time the combo box automatically closes
dropDwn.duration = 3;
*/
addChild(dropDwn);
dropDwn.addEventListener(Event.CHANGE,onDropDown);
dropDwn2 = new DropDown();
dropDwn2.x = 225
dropDwn2.y = 200;
addChild(dropDwn2);
dropDwn2.init(dropDArray2,150, 25,"up");
dropDwn2.addEventListener(Event.CHANGE,onDropDown);
createStatusField();
createButton();
}
/*
Method:createButton
Parameters:
Returns:
*/
private function createButton():void
{
var updateButton = new Button();
updateButton.x = 200;
updateButton.y = 28;
updateButton.label = "Update Array";
updateButton.addEventListener(MouseEvent.CLICK,updateArray);
addChild(updateButton);
}
/*
Method:updateArray
Parameters:
Event:MouseEvent
Returns:
*/
private function updateArray(Event:MouseEvent):void
{
if(currArray==0){
dropDwn.dataProvider = dropDArray3;
currArray =3;
}else{
dropDwn.dataProvider = dropDArray;
currArray =0;
}
}
/*
Method:createStatusField
Parameters:
Returns:
*/
private function createStatusField():void
{
t = new TextField();
t.name = "tField";
t.x = 5;
t.y = 250;
t.selectable = false;
t.autoSize = TextFieldAutoSize.LEFT;
addChild(t);
}
/*
Method:onDropDown
Parameters:
event:Event
Returns:
*/
private function onDropDown(event:Event)
{
t.text = "label : " + event.target.selectedObject.label + "\ndata : " + event.target.selectedObject.data;
}
}
}
So the line of code : dropDwn = new DropDown(dropDArray,150, 25);
The line of code dropDwn = new DropDown();
Attaches the DropDown Movieclip from the library to the stage.
The line of code dropDwn.init(dropDArray,150, 25); instantiates the comb box with the following parameters.
The first param is an array of objects {label:”label name”,data:0}
The second is the width:Number.
The third is the height:Number.
The fourth param is an OPTIONAL String, by default its set to drop “down” but could be set to “up”.
As one would assume the width and height change the background width and height and the menu item background width and height.
A few lines down we add a listener to the combo box:
dropDwn.addEventListener(Event.CHANGE,onDropDown);
Which calls the onDropDown method, when a user selects a menu item.
The second instantiation of:
/*
dropDwn2 = new DropDown(dropDArray2,150, 25,”up”);
dropDwn2.x = 225
dropDwn2.y = 200;
addChild(dropDwn2);
dropDwn2.addEventListener(Event.CHANGE,onDropDown);
*/
merely displays an example of the drop downs flexibility, where you can decide if you want it to drop “down” or “up”.
The call to createStatusField() method is not needed, its just being implemented as a debug window for the example app.
The call to the createButton() method simply creates the button to change the data provider to the drop down menu. This was added as an update to the original post. When I actually used the drop down menu in my project, I needed the ability to update the drop down menu on the fly.
Now for the flexibility….
The commented out code explains what you can do to change the look and feel of the combo box.
Lets Review:
Using an embeded font:
To use an embeded font you have to create a font symbol in the library, give it a linkage identifier and export it. If your not familiar with the process here’s some simple steps to get you on your way.
1. Open the library panel.
2. Select New Font from the Library Panel menu.
3. Enter a name for the font in the Name field.
4. Select a font from the Font menu or enter the name of a font in the Font field.
5. (Optional) Select your desired font style (Regular, Italic, Bold, or Bold Italic) from the Style drop-down menu.
6. Click OK.
Before you can instantiate a new Font instance dynamically, you need to make sure it has a linkage class name defined.
To set a linkage class:
1. Right-click the font symbol in the library and select Linkage.
2. Click Export for ActionScript and enter a class name . In my example, I named the font HelveBold.
3. Click OK.
Then all you have to do is add these lines of code
var helve:Font = new HelveBold(); //new LINKAGE_NAME_OF_YOUR_FONT();
dropDwn.customFont = helve;
Change the font size:
Just add the line of code.
dropDwn.fontSize = 9;
*Note- the number you pass in, is the size in which the text in the menu item will be displayed.
I add on 2 more pixels for the label text.
Change the font:
If you didn’t choose to embed your font and want to rely on system fonts you simply just pass in the name of the font as a string. If the users computer has the font installed, the text will display correctly.
dropDwn.fontName = "Times";
*Note – by default the font is set to Arial.
Change the menu item text color:
You have control of the default color and the rollover color for the text in the menu item.
Simply add the line of code, and pass in an array with both colors, as shown below.
The default color is also used for the combo box label.
//(DefaultColor,OverColor)
dropDwn.itemFontColors = new Array(0xff0000,0x00ff00);
Change the menu item background color:
You have control of the menu item background color for both default and rollover states.
Just add the line of code, and pass in an array with both colors, as shown below.
//(DefaultColor,OverColor)
dropDwn.itemColors = new Array(0xff0000,0x0000ff);
Change the time the combo box automatically closes if there is no interaction:
By default the time is set to 2 seconds..
Just add the line of code below to change it to 3 seconds.
dropDwn.duration=3;
Change the state from drop down to up:
You have control in cases where the combo box height will run off stage, so if you need it to display UP instead of down, just add the string “up” when you instantiate, like so:
dropDwn = new DropDown(dropDArray,150, 25,"up");
Last but not least, the code that makes the combo box work. I’m not going to go into detail or even explain the code, its magic that you can sort out on your own.
DropDown.as
/****************************
* Manuel Gonzalez *
* design@stheory.com *
* www.stheory.com *
* www.codingcolor.com *
*****************************/
package com.dropdown{
import flash.display.Sprite;
import flash.display.MovieClip;
import flash.display.DisplayObject;
import flash.events.TimerEvent;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.text.TextField;
import flash.text.TextFormat;
import flash.text.TextFieldAutoSize;
import flash.text.Font;
import flash.utils.Timer;
import flash.geom.ColorTransform;
public class DropDown extends MovieClip {
private var _dp:Array;
private var _w:Number;
private var _h:Number;
private var _timer:Timer;
private var _open:Boolean = false;
private var _menuCreated:Boolean = false;
private var _direction:String;
private var _labelField:TextField;
private var _hit:Sprite;
//
private var _selectedObj:Object;
private var _lastSelectedItem:MovieClip;
private var _selectedIndex:int = 0;
private var _timerDuration:Number = 2000;
//
private var _fontDefaultColor:uint = 0x000000;
private var _fontOverColor:uint = 0x0000ff;
private var _fontSize:Number = 12;
private var _fontName:String = "Arial";
private var _customFont:Font;
//
private var _itemDefaultBkgColor:uint = 0xCCCCCC;
private var _itemOverBkgColor:uint = 0xFFFFFF;
public function set dataProvider(inDp:Array):void
{
if(_menuCreated){
refreshMenu();
}
_dp = inDp;
_lastSelectedItem = null;
createDropDownMenu();
setSelectedFlag(_dp[_selectedIndex].mc)
setlabel(_dp[_selectedIndex].label)
}
public function get dataProvider():Array
{
return _dp;
}
public function set selectedIndex(inInt:int):void
{
_selectedIndex = inInt;
}
public function get selectedIndex():int
{
return _selectedIndex;
}
public function set selectedObject(inObj:Object):void {
_selectedObj = inObj;
}
public function get selectedObject():Object {
return _selectedObj;
}
public function set duration(inNum:Number):void {
_timerDuration = inNum*1000;
}
public function get duration():Number {
return _timerDuration;
}
public function set itemColors(inArray:Array):void
{
_itemDefaultBkgColor = inArray[0];
_itemOverBkgColor = inArray[1];
if(_menuCreated){
setItemColor();
}
}
public function get itemColors():Array
{
return [_itemDefaultBkgColor,_itemOverBkgColor];
}
public function set itemFontColors(inArray:Array):void
{
_fontDefaultColor = inArray[0];
_fontOverColor = inArray[1];
if(_menuCreated){
setFont();
}
}
public function get itemFontColors():Array
{
return [_fontDefaultColor,_fontOverColor];
}
public function set fontSize(inNum:Number):void
{
_fontSize = inNum;
if(_menuCreated){
setFont();
}
}
public function get fontSize():Number
{
return _fontSize;
}
public function set fontName(inStr:String):void
{
_fontName = inStr;
if(_menuCreated){
setFont();
}
}
public function get fontName():String
{
return _fontName;
}
public function set customFont(inFont:Font):void
{
_customFont = inFont;
if(_menuCreated){
setFont();
}
}
public function get customFont():Font
{
return _customFont;
}
/* Constructor*/
public function DropDown() {
}
/*
Method:init
Parameters:
inDataProvider:Array
w:Number
h:Number
direction:String="down"
Returns:
*/
public function init(inDataProvider:Array, w:Number, h:Number,direction:String="down"):void
{
_w = w;
_h = h;
_dp = inDataProvider;
_direction = direction;
_timer = new Timer(_timerDuration);
_timer.addEventListener( TimerEvent.TIMER, checkDropDown);
_selectedObj = _dp[_selectedIndex];
adjustBackground();
adjustArrow();
createDropDownMenu();
createlabelField();
createHit();
setSelectedFlag(_dp[_selectedIndex].mc)
}
/*
Method:createHit
Parameters:
Returns:
*/
private function createHit():void
{
_hit = new Sprite();
_hit.graphics.clear();
_hit.graphics.beginFill(0x000000, 1 );
_hit.graphics.drawRect( 0, 0, _w, _h);
_hit.graphics.endFill();
_hit.alpha=0;
addChild(_hit);
_hit.buttonMode = true;
_hit.addEventListener( MouseEvent.CLICK, openDropDown );
}
/*
Method:createlabelField
Parameters:
Returns:
*/
private function createlabelField():void
{
_labelField = new TextField();
_labelField.selectable = false;
_labelField.autoSize = TextFieldAutoSize.LEFT;
_labelField.x = 5;
addChild(_labelField)
var format:TextFormat = new TextFormat();
format.color = _fontDefaultColor;
format.size = _fontSize + 2;
if(_customFont == null)
{
format.font = _fontName;
}else{
format.font = _customFont.fontName;
_labelField.embedFonts = true;
}
_labelField.defaultTextFormat = format;
setlabel(_selectedObj.label);
_labelField.y = ( ddBkg.height/2 ) - ( _labelField.height/2);
}
/*
Method:adjustLabel
Parameters:
Returns:
*/
private function adjustLabel():void
{
var format:TextFormat = new TextFormat();
format.color = _fontDefaultColor;
format.size = _fontSize + 2;
if(_customFont == null)
{
format.font = _fontName;
}else{
format.font = _customFont.fontName;
_labelField.embedFonts = true;
}
_labelField.defaultTextFormat = format;
setlabel(_selectedObj.label);
_labelField.y = ( ddBkg.height/2 ) - ( _labelField.height/2 );
}
/*
Method:setlabel
Parameters:
inStr:String
Returns:
*/
private function setlabel(inStr:String):void {
_labelField.text = inStr;
}
/*
Method:adjustBackground
Parameters:
Returns:
*/
private function adjustBackground():void {
ddBkg.width =_w;
ddBkg.height = _h;
}
/*
Method:adjustArrow
Parameters:
Returns:
*/
private function adjustArrow():void {
if ( _direction == "up" ) {
ddArrow.rotation = 180;
}
ddArrow.x = ddBkg.x + ddBkg.width - 15;
ddArrow.y = ddBkg.height/2;
}
/*
Method:formatField
Parameters:
inTf:TextField
inColor:uint
Returns:
*/
private function formatField(inTf:TextField,inColor:uint):void {
var format:TextFormat = new TextFormat();
if(_customFont == null)
{
format.font = _fontName;
}else{
format.font = _customFont.fontName;
inTf.embedFonts = true;
}
format.color = inColor;
format.size = _fontSize;
inTf.setTextFormat(format);
}
/*
Method:checkDropDown
Parameters:
event:TimerEvent
Returns:
*/
private function checkDropDown(event:TimerEvent):void {
if (_open) {
closeDropDown();
}
}
/*
Method:refreshMenu
Parameters:
Returns:
*/
private function refreshMenu():void
{
var dpLen:uint = _dp.length
for (var i=0; i< dpLen; i++) {
removeItemListeners(_dp[i].mc);
var MC = _dp[i].mc;
removeChild(MC);
}
_dp = [];
}
/*
Method:createDropDownMenu
Parameters:
Returns:
*/
private function createDropDownMenu():void {
var dpLen:uint = _dp.length
for (var i=0; i< dpLen; i++) {
var container:MovieClip = new MovieClip();
container.name = _dp[i].label;
container.obj = _dp[i];
container.isSelected =false;
var back:Sprite = new Sprite();
back.name = "bkg";
back.graphics.clear();
back.graphics.beginFill( _itemDefaultBkgColor, 1 );
back.graphics.drawRect( 0, 0, _w, _h);
back.graphics.endFill();
back.mouseEnabled = false;
container.spr = back;
container.addChild(back);
var t = new TextField();
t.mouseEnabled = false;
t.name = "tField";
t.x = 5;
t.selectable = false;
t.autoSize = TextFieldAutoSize.LEFT;
t.text = _dp[i].label;
formatField(t,_fontDefaultColor);
t.y = ( back.height/2 ) - ( t.height/2 );
container.tField = t;
container.visible = false;
addItemListeners(container)
container.addChild( t );
addChild(container);
_dp[i].mc = container;
}
_menuCreated = true;
}
/*
Method:addItemListeners
Parameters:
inMC:MovieClip
Returns:
*/
private function addItemListeners(inMC:MovieClip):void
{
inMC.buttonMode = true;
inMC.addEventListener( MouseEvent.CLICK, itemSelected ,false,0,true);
inMC.addEventListener( MouseEvent.MOUSE_OUT, itemOut,false,0,true);
inMC.addEventListener( MouseEvent.MOUSE_OVER, itemOver,false,0,true);
}
/*
Method:removeItemListeners
Parameters:
inMC:MovieClip
Returns:
*/
private function removeItemListeners(inMC:MovieClip):void
{
inMC.buttonMode = false;
inMC.removeEventListener( MouseEvent.CLICK, itemSelected );
inMC.removeEventListener( MouseEvent.MOUSE_OUT, itemOut);
inMC.removeEventListener( MouseEvent.MOUSE_OVER, itemOver);
}
/*
Method:setFont
Parameters:
Returns:
*/
private function setFont():void
{
for (var i=0; i<_dp.length; i++) {
var item:MovieClip = _dp[i].mc;
var t:TextField = item.tField;
formatField(t,_fontDefaultColor);
}
adjustLabel();
}
/*
Method:setItemColor
Parameters:
Returns:
*/
private function setItemColor():void
{
for (var i=0; i<_dp.length; i++)
{
var item:MovieClip = _dp[i].mc;
item.removeChild(item.spr )
var back:Sprite = new Sprite();
back.name = "bkg";
back.graphics.clear();
if(item.isSelected){
back.graphics.beginFill( _itemOverBkgColor, 1 );
}else{
back.graphics.beginFill( _itemDefaultBkgColor, 1 );
}
back.graphics.drawRect( 0, 0, _w, _h);
back.graphics.endFill();
item.spr = back;
item.addChildAt(back,0);
}
}
/*
Method:itemOver
Parameters:
event:Event
Returns:
*/
private function itemOver(event:Event):void {
_timer.stop();
changeItemBackgroundColor(event.target.spr,_itemOverBkgColor);
formatField(event.target.tField,_fontOverColor);
}
/*
Method:itemOut
Parameters:
event:Event
Returns:
*/
private function itemOut(event:Event):void {
changeItemBackgroundColor(event.target.spr,_itemDefaultBkgColor);
formatField(event.target.tField,_fontDefaultColor);
_timer.start();
}
/*
Method:changeItemBackgroundColor
Parameters:
inDo:DisplayObject
inColor:uint
Returns:
*/
private function changeItemBackgroundColor(inDo:DisplayObject,inColor:uint):void {
var _color:ColorTransform = inDo.transform.colorTransform;
_color.color = inColor;
inDo.transform.colorTransform = _color;
}
/*
Method:itemSelected
Parameters:
event:Event
Returns:
*/
private function itemSelected(event:Event):void {
_timer.stop();
var tLbl:String = event.target.obj.label;
_selectedObj = event.target.obj;
var mc:MovieClip = event.target as MovieClip;
setSelectedFlag(mc)
setlabel(tLbl);
closeDropDown();
dispatchEvent(new Event(Event.CHANGE));
}
/*
Method:setSelectedFlag
Parameters:
inMC:MovieClip
Returns:
*/
private function setSelectedFlag(inMC:MovieClip):void
{
if(_lastSelectedItem != null){
_lastSelectedItem.isSelected = false;
changeItemBackgroundColor(_lastSelectedItem.spr,_itemDefaultBkgColor);
formatField(_lastSelectedItem.tField,_fontDefaultColor);
addItemListeners(_lastSelectedItem);
}
inMC.isSelected = true;
removeItemListeners(inMC);
changeItemBackgroundColor(inMC.spr,_itemOverBkgColor);
formatField(inMC.tField,_fontOverColor);
_lastSelectedItem = inMC;
}
/*
Method:openDropDown
Parameters:
Returns:
*/
private function openDropDown( event:Event ) {
if ( !_open ) {
for (var i=0; i<_dp.length; i++) {
var item:DisplayObject = _dp[i].mc;
item.alpha = 0;
item.visible = true;
if ( _direction == "down" ) {
item.y = _h + ( _h * i );
item.alpha =1;
} else {
item.y = -_h - ( _h * i );
item.alpha = 1;
}
}
_open = true;
} else {
closeDropDown();
}
_timer.start();
}
/*
Method:closeDropDown
Parameters:
Returns:
*/
private function closeDropDown() {
if ( _open ) {
_timer.stop();
for (var i=0; i<_dp.length; i++) {
var item:DisplayObject = _dp[i].mc;
item.visible = false;
item.y =0;
item.alpha=0;
}
}
_open = false;
}
}
}
There you go.
Enjoy!
Download Source:
AS3 custom drop down (54)