It's been quite a while since I blogged last--for that I apologize.
When you create a UI component in Flex, there are 4 main methods we ask that you implement: createChildren, commitProperties, validateSize, and updateDisplayList. The Flex framework then calls these methods at the appropriate times, but if you get into component development, you need to understand not only how these mechanisms work, but why we have these mechanisms in the first place. As a new member to the Flex SDK team, this was one of the first questions I had. To help answer this, let's take a look at something I'm currently implementing. Note that whatever I show may or may not end up being the final implementation, but it's a good example to look at.
In AIR (Adobe Integrated Runtime), they added NativeMenu's, which are menus native to each operating system and sit at the top of the screen for a window. The API for this is centered around instantiating Menus, MenuItems, and adding them all together. A typical example would look like:
var mainMenu:NativeMenu = new NativeMenu();
var fileMenu:NativeMenu = new NativeMenu();
var printItem:NativeMenuItem = new NativeMenuItem("Print");
printItem.keyEquivalent = "P";
printItem.keyEquivalentModifiers = [Keyboard.CONTROL];
printItem.mnemonicIndex = 0;
...
var saveItem:NativeMenuItem = new NativeMenuItem("Save");
...
var newItem:NativeMenuItem = new NativeMenuItem("New");
...
fileMenu.addItem(printItem);
fileMenu.addItem(saveItem);
fileMenu.addItem(newItem);
var fileMenuItem:NativeMenuItem = new NativeMenuItem("File");
fileMenuItem.subMenu = fileMenu;
mainMenu.addItem(fileMenuItem);
// repeat for Edit, View, etc...
[Bindable] private var myMenuData:XMLList = <> <menuitem label="_File">yy <menuitem label="_Save" keyEquivalent="F10" /> <menuitem label="P_rint" mnemonicIndex="4" mnemonicIndexOverride="true" /> <menuitem type="separator" /> <menuitem label="E_xit" /> </menuitem> <menuitem label="_Edit"> <menuitem label="_Copy" toggled="true" type="check" /> <menuitem label="C_ut"/> <menuitem label="_Paste" /> </menuitem> </>; <mx:Desktopmenu id="desktopMenu" dataProvider="{myMenuData}" labelField="@label" showRoot="false"/>
public function set dataProvider(value:Object):void { _dataProvider = value; createNativeMenu(); }
public function set dataProvider(value:Object):void { _dataProvider = value; invalidateProperties(); } private invalidatePropertiesFlag:Boolean = false; private function invalidateProperties() { if (!invalidatePropertiesFlag) { invalidatePropertiesFlag = true; var myTimer:Timer = new Timer(100, 1); myTimer.addEventListener(TimerEvent.TIMER, validateProperties); myTimer.start(); } } private function validateProperties(event:TimerEvent) { if (invalidatePropertiesFlag) { invalidatePropertiesFlag = false; createNativeMenu(); } }
<?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" applicationComplete="init()" layout="absolute" > <mx:Script> <![CDATA[ private function init():void { stage.frameRate = 1; } private function width50():void { myCanvas.width = 50; var myTimer:Timer = new Timer(500, 1); myTimer.addEventListener(TimerEvent.TIMER, width200); myTimer.start(); } private function width200(event:TimerEvent):void { myCanvas.width = 200; } ]]> </mx:Script> <mx:Canvas id="myCanvas" backgroundColor="red" width="100" height="100" /> <mx:Button label="Click Me" click="width50()" x="200" y="200" /> </mx:Application>
We change the framerate to 1 per second. Now, when you click the button, sometimes you'll see it shrink to a width of 50 before growing to 200, or sometimes you'll see it just go to 200. It depends on exactly when you click on the button with respect to where in the current frame we are. There are a few reasons the flash player does this as it helps with performance, but the important point is that Flex ties into this and does validation/re-measuring only when it'll actually affects the screen by tying in to the ENTER_FRAME event. So, in this DesktopMenu component, we should do the same thing and validate ourselves when all other components do (to be honest, I'm not sure if the native menu, since it's using the operating system API underneath can update itself in between frames or not). To do this, we need to implement ILayoutManagerClient, like UIComponent does, and then attach ourselves to the LayoutManager's queue by calling UIComponentGlobals.mx_internal::layoutManager.invalidateProperties(this);. Since we're not a display object, we don't need to do anything in validateSize or validateDisplayList. One thing to note is that despite it's name, LayoutManger should really be though of as a ValidationManager because it doesn't just deal with layouts. One other reason we do this is because sometimes the order of properties matters, and if it's represented as MXML, this order can be lost. So for example, in a List component, we might do something like:
myList.dataProvider = ["a", "b", "c"]; // gets promoted to an ArrayCollection myList.selectedIndex = 1;
But in MXML, you have:
<mx:List id="myList" dataProvider="{['a', 'b', 'c']}" selectedIndex="1" />
<mx:List id="myList" selectedIndex="1" dataProvider="{['a', 'b', 'c'}" />
- Properties tend to change at the same time, and we don't want to re-do the same work because of this.
- Property changes often cascade and affect many other properties (changing my position, would affect lots of other elements). This exacerbates the point made above.
- The Flash Player uses delayed rendering, so changing a UI property doesn't actually display until the next frame
- MXML representations of objects don't take into account order of operations.
4 comments:
Excellent article!
I wished I had read this before I took on my first Flex project. I remember tearing my hair out, setting up all kinds of Timers etc. to get my "components" to update as I needed, only to discover that there was this near genius mechanism that does it for you :)
I like how you've explained it here. Incidentally, I'm giving a presentation dedicated to this very topic at Flex360 Europe in april. Your post has given me some good ideas, Thanks!
cheers, Sakri
Good brief and this post helped me alot in my college assignement. Thanks you as your information.
Good dispatch and this post helped me alot in my college assignement. Say thank you you seeking your information.
Complex Post. This record helped me in my school assignment. Thanks Alot
Post a Comment