AS3 Drop Down Menu

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:

Get Adobe Flash player

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 (1240)

5 thoughts on “AS3 Drop Down Menu

  • November 17, 2012 at 1:08 pm
    Permalink

    I attempted to get your model working but found ddBkg is not defined anywhere.

    Reply
    • February 1, 2013 at 1:53 pm
      Permalink

      Did you get this working? Sorry for the late reply, I have been really busy and have not been able to get back to my blog.

      Reply
  • October 30, 2013 at 7:21 am
    Permalink

    Million Thanks for your great work! It is very well written and saved my life when I am working on a very urgent project!

    Reply
  • November 29, 2013 at 9:11 am
    Permalink

    The code seems not to work. Its says on line 212 and 238 that ddBkg is not defined. And when i checked through the class. I observe the variable has not been declared any where within the class. Please can u send an update file to me. Thanks in advance

    Reply
  • December 4, 2013 at 4:11 am
    Permalink

    our comment is awaiting moderation.

    The code seems not to work. Its says on line 212 and 238 that ddBkg is not defined. And when i checked through the class. I observe the variable has not been declared any where within the class. Please can u send an update file to me. Thanks in advance

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

%d bloggers like this: