October 8th, 2007 by Kyle
Posted in: Flex, actionscript
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.
<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:
{
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:
<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>





November 19th, 2007 at 7:35 pm
compiles fine with flex 2.0.1. by flex 3 beta 2 we have some additional methods to implement.
i’ve been gearing toward menu skinning. will let you know if your class helps.