I had a customer that was trying to use masking to make menus appear with rounded corners. I had gotten this to work, but there was something I could not figure out that caused a flicker as menus were selected and appeared. I put this on the back shelf for a while and recently revisited it. To my surprise, the issue I had previously seen had gone away. Either something in the Flex framework had changed or a more recent version of the player had a beneficial effect. Whatever the cause, I decided it was good enough and that I should post this code.

In this sample, I extended the default MenuBar, so I could get a handle on new menus as they are shown to apply the mask. I also implemented a new style “corner-radius” and a custom menuItem renderer.

Browse the source of this example.
Download a zipfile containing the source to this sample.

This movie requires Flash Player 9


Here is the code for the app:

<?xml version="1.0"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
    creationComplete="initCollections()" xmlns="*"
    viewSourceURL="wp-content/code/Flex2.0.1hf2/739stl_RoundedMenus/srcview/index.html">

    <mx:Style>
        MyMenuBar {
            padding-top: 0; padding-left: 0; padding-right: 0;   
            padding-bottom: 0; border-thickness: 0; borderStyle: "none";
        }
        .MyMenuStyle {
            corner-radius: 20;
            padding-top: 0; padding-left: 0; padding-right: 0;   
            padding-bottom: 0; border-thickness: 0; borderStyle: "none";
        }
    </mx:Style>
   
    <mx:Script>
        <![CDATA[
            import mx.controls.Alert;
            import mx.collections.*;

            [Bindable]
            public var menuBarCollection:XMLListCollection;
   
            private var menubarXML:XMLList =
                <>
                    <menuitem label="Menu1" data="top">
                        <menuitem label="MenuItem 1-A" key="1A"/>
                        <menuitem label="MenuItem 1-B" key="1B"/>
                    </menuitem>
                    <menuitem label="Menu2" data="top">
                        <menuitem label="MenuItem 2-A" key="2A"/>
                        <menuitem label="MenuItem 2-B" >
                            <menuitem label="SubMenuItem 3-A" key="3A"/>
                            <menuitem label="SubMenuItem 3-B" key="3B"/>
                        </menuitem>
                    </menuitem>
                </>;

            private function initCollections():void {
                menuBarCollection = new XMLListCollection(menubarXML);
            }
           
         ]]>
    </mx:Script>
    <MyMenuBar labelField="@label"
        dataProvider="{menuBarCollection}"/>

</mx:Application>
 

Here is the code for the extension to the default menubar:

package
{
    import mx.controls.MenuBar;
    import mx.controls.Menu;
    import mx.events.MenuEvent
    import flash.display.Sprite;
    import mx.core.ClassFactory;

    public class MyMenuBar extends MenuBar
    {
        public function MyMenuBar() {
            super();
            addEventListener("menuShow", onMenuShow);
        }

        override public function getMenuAt(index:int) : Menu {
            var menu:Menu = super.getMenuAt(index);
            menu.styleName = "MyMenuStyle";
            menu.itemRenderer = new ClassFactory(MyMenuItemRenderer);
            menu.setStyle("openDuration",0);
            menu.cacheAsBitmap=false;
      return menu;
        }
       
        private function onMenuShow(e:MenuEvent):void{
            callLater(maskRoundedCorners,[e]);
        }
       
        private function maskRoundedCorners(e:MenuEvent):void{
               
                var menu:Menu=e.menu as Menu;
                menu.cacheAsBitmap=false;

                if (!menu.mask){
                    var maskx:uint = menu.x;
                    var masky:uint = menu.y;
                    var maskw:uint = menu.getExplicitOrMeasuredWidth();
                    var maskh:uint = menu.getExplicitOrMeasuredHeight();
                    var rad:int = menu.getStyle("cornerRadius") * 2;
                   
                    var roundRect:Sprite = new Sprite();
                    roundRect.graphics.beginFill(0xFFFFFF);
                    roundRect.graphics.drawRoundRect(maskx,masky,maskw,maskh,rad);
                    roundRect.graphics.endFill();
                    menu.mask = roundRect;
            }
        }     
    }
}
 

Here is the code for the custom menuItemRenderer:

<?xml version="1.0" encoding="utf-8"?>
<mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml"
    implements="mx.controls.listClasses.IListItemRenderer,
                mx.controls.menuClasses.IMenuItemRenderer,
                mx.controls.listClasses.IDropInListItemRenderer"

                horizontalScrollPolicy="off">
   
    <mx:Script>
    <![CDATA[
        import mx.controls.Alert;
        import mx.controls.menuClasses.IMenuDataDescriptor;
        import mx.controls.listClasses.BaseListData;
        import mx.controls.listClasses.ListData;
        import mx.controls.Menu;
        import mx.controls.menuClasses.MenuBarItem;  
       
        private var _menu:Menu;
        private var _listData:ListData;
       
        private var lblText:String;
        private var keyText:String;
        private var isBranch:Boolean;
        
       
        override public function set data(value:Object) : void {
            super.data = value;
           
            this.lblText = this.data.@label;
            this.keyText = this.data.@key;

            var dataDescriptor:IMenuDataDescriptor =
                    Menu(_listData.owner).dataDescriptor;
           
            this.isBranch = dataDescriptor.isBranch(super.data);
           
            invalidateProperties();
            invalidateSize();         

        }
       
        public function get menu() : Menu {
            return _menu;
        }
   
        public function set menu( value:Menu ) : void {
            _menu = value;
        }   
       
        public function get listData() : BaseListData {
            return _listData;
        }
   
        public function set listData( value:BaseListData ) : void {
            _listData = ListData(value);
            invalidateProperties();
        }
       
        override public function setActualSize(w:Number, h:Number) : void {
            super.setActualSize( Menu(_listData.owner).width, h );
        }
       
        override protected function commitProperties() : void {
           
            super.commitProperties();
   
            // Branch icon
            if ( isBranch ) {
                    arrow.visible=true;
                    arrow.includeInLayout=true;
                }
            else{
                arrow.visible=false;
                arrow.includeInLayout=false;
            }

            lbl.text=lblText;
            key.text=keyText;
        }
    ]]>
    </mx:Script>
   
    <mx:Label id="lbl"
        width="100%"
        textAlign="left"
        paddingLeft="8"
        paddingRight="6"/>
       
    <mx:Label id="key"
        minWidth="30"
        paddingRight="10"
        textAlign="right"
        fontStyle="italic" />

    <mx:Image id="arrow" width="20" source="@Embed(source=’Assets.swf’,symbol=’MenuBranchEnabled’)"
        visible="true" />

       
</mx:HBox>
 

Share and Enjoy: These icons link to social bookmarking sites where readers can share and discover new web pages.
  • digg
  • Technorati
  • Reddit
  • del.icio.us
  • Slashdot