Pages

November 10, 2010

Flex 4 Skinning for Layout Reuse

Posted by Jeremy Mitchell
In a prior entry entitled "Sharing MXML Layout Across Components", I demonstrated a technique used to share one MXML layout definition across many components.

However, with the advent of Flex 4 and its skinning architecture, a better way exists.

You'll remember that we had two editors (a report editor and a list editor) similar in layout and structure. Each editor also shared a couple of properties and methods. Fortunately, the Flex 4 skinning architecture makes sharing layout across components a snap. Here's a high-level overview of the chosen architecture:

And here's the application:



View source is enabled.

To build this, I first created a base class called Editor. This base class defines the shared properties and methods of each editor. Here was my first pass at the Editor class:
<!-- Editor.mxml -->

<?xml version="1.0" encoding="utf-8"?>
<s:Panel xmlns:fx="http://ns.adobe.com/mxml/2009"
         xmlns:s="library://ns.adobe.com/flex/spark"
         xmlns:mx="library://ns.adobe.com/flex/mx"
         width="500"
         title="{editorType} Editor"
         preinitialize="onPreinitialize(event)">

    <fx:Script>
        <![CDATA[
            import mx.events.FlexEvent;

            public static const REPORT:String="Report";
            public static const LIST:String="List";

            [Bindable]
            public var editorType:String;

            protected function save():void
            {
                throw new Error("Abstract Class - override this method in a subclass.");
            }

            protected function onPreinitialize(event:FlexEvent):void
            {
                // subclass should override this method and set the editorType
                throw new Error("Abstract Class - override this method in a subclass.");
            }
        ]]>
    </fx:Script>

</s:Panel>
Next, I created a skin (New > MXML Skin) based on the Editor class (aka Host Component). Since Editor is a subclass of Panel, I was presented with the option to "Create as copy of spark.skins.spark.PanelSkin". I ticked the checkbox.

With a starting point for my skin (EditorSkin.mxml), I made the proper adjustments to support my editor's custom layout. Specifically, I wrapped the content group (id == contentGroup) with additional layout code. Here was the skin's content group before:
<s:Group id="contentGroup" width="100%" height="100%" 
   minWidth="0" minHeight="0">
</s:Group>
Here is the content group after my adjustments:
<s:Group id="editorLayout"
   width="100%">
 
 <s:layout>
  <s:VerticalLayout paddingTop="10"
        paddingBottom="10"
        paddingLeft="10"
        paddingRight="10"/>
 </s:layout>
 
 <s:BorderContainer width="100%"
        height="30"
        borderWeight="1"
        borderColor="#aab3b3"
        backgroundColor="#ededed">
  
  <s:layout>
   <s:HorizontalLayout verticalAlign="middle"
        paddingLeft="10"
        paddingRight="10"/>
  </s:layout>
  
  <s:HGroup id="toolBarChildrenGroup"/>
  
  <s:Rect width="100%"/>
  
  <s:Label text="{hostComponent.editorType}"
     fontWeight="bold"/>
  
  
 </s:BorderContainer>
 
 <!--- @copy spark.components.SkinnableContainer#contentGroup -->
 <s:Group id="contentGroup"
    width="100%"
    height="100%"
    minWidth="0"
    minHeight="0">
  
  <s:layout>
   <s:VerticalLayout/>
  </s:layout>
  
 </s:Group>
 
</s:Group>
Then, I mapped the skin class to a CSS style declaration within Styles.css (and don't forget to include the CSS file in your application!).
.editor
{
    skin-class: ClassReference( "skins.EditorSkin" );
}
Finally, the host component (Editor) needed to be altered to accommodate my new skin. First, it needed the proper styleName value to utilize the skin. It also needed to manually inject tool bar contents into the skin. This required three things:

  1. A public property to hold each editor's custom tool bar contents
  2. A declaration of a required skin part
  3. An override of the partAdded() method

Here was my second pass at the Editor class:
<!-- Editor.mxml -->

<?xml version="1.0" encoding="utf-8"?>
<s:Panel xmlns:fx="http://ns.adobe.com/mxml/2009"
   xmlns:s="library://ns.adobe.com/flex/spark"
   xmlns:mx="library://ns.adobe.com/flex/mx"
   width="500"
   title="{editorType} Editor"
   styleName="editor"
   preinitialize="onPreinitialize(event)">
 
 <fx:Script>
  <![CDATA[
   import mx.events.FlexEvent;
   
   import spark.components.HGroup;
   
   public static const REPORT:String="Report";
   public static const LIST:String="List";
   
   // the custom tool bar contents will be injected into this HGroup
   // the horizontal layout of the tool bar is enforced by the skin having an HGroup
   [SkinPart(required="true")]
   public var toolBarChildrenGroup:HGroup;
   
   [Bindable]
   public var editorType:String;
   
   // this is where custom tool bar contents will reside
   public var toolBarChildren:Array;
   
   override protected function partAdded(partName:String, instance:Object):void
   {
    super.partAdded(partName, instance);
    
    // when the toolBarChildrenGroup skin part is added, everything inside the 
    // toolBarChildren array will be "injected" into the toolBarChildrenGroup HGroup
    if (instance == toolBarChildrenGroup)
    {
     toolBarChildrenGroup.mxmlContent=toolBarChildren;
    }
   }
   
   protected function save():void
   {
    throw new Error("Abstract Class - override this method in a subclass.");
   }
   
   protected function onPreinitialize(event:FlexEvent):void
   {
    // subclass should override this method and set the editorType
    throw new Error("Abstract Class - override this method in a subclass.");
   }
  ]]>
 </fx:Script>
 
</s:Panel>
Then I simply extended the Editor class and added the content specific to the subclass.
<!-- ReportEditor.mxml -->

<?xml version="1.0" encoding="utf-8"?>
<comps:Editor xmlns:fx="http://ns.adobe.com/mxml/2009"
              xmlns:s="library://ns.adobe.com/flex/spark"
              xmlns:mx="library://ns.adobe.com/flex/mx"
              xmlns:comps="comps.*">

    <fx:Script>
        <![CDATA[
            import mx.events.FlexEvent;

            protected function saveButton_clickHandler(event:MouseEvent):void
            {
                save();
            }

            protected function publishButton_clickHandler(event:MouseEvent):void
            {
                publish();
            }

            protected function publish():void
            {
                //publish
            }

            override protected function save():void
            {
                //save
            }

            override protected function onPreinitialize(event:FlexEvent):void
            {
                editorType=Editor.REPORT;
            }
        ]]>
    </fx:Script>

    <comps:toolBarChildren>

        <!-- put whatever you like in here. buttons, comboboxes, etc
             but whatever you put in here gets laid out horizontally
             inside a border / background. the subclass defines the tool 
             bar contents, not the super class -->

        <s:Button label="Save"
                  click="saveButton_clickHandler(event)"/>

        <s:Button label="Publish"
                  click="publishButton_clickHandler(event)"/>

    </comps:toolBarChildren>

    <!-- Anything you put here gets "injected" into the skin's contentGroup -->

    <comps:ReportForm/>

</comps:Editor>
<!-- ListEditor.mxml -->

<?xml version="1.0" encoding="utf-8"?>
<comps:Editor xmlns:fx="http://ns.adobe.com/mxml/2009"
              xmlns:s="library://ns.adobe.com/flex/spark"
              xmlns:mx="library://ns.adobe.com/flex/mx"
              xmlns:comps="comps.*">

    <fx:Script>
        <![CDATA[
            import mx.events.FlexEvent;

            protected function saveButton_clickHandler(event:MouseEvent):void
            {
                save();
            }

            protected function submitButton_clickHandler(event:MouseEvent):void
            {
                submit();
            }

            protected function submit():void
            {
                //submit
            }

            override protected function save():void
            {
                //save
            }

            override protected function onPreinitialize(event:FlexEvent):void
            {
                editorType=Editor.LIST;
            }
        ]]>
    </fx:Script>

    <comps:toolBarChildren>

        <s:Button label="Save"
                  click="saveButton_clickHandler(event)"/>

        <s:Button label="Submit"
                  click="submitButton_clickHandler(event)"/>

    </comps:toolBarChildren>

    <comps:ListForm/>

</comps:Editor>
You can also watch me demonstrate this technique in this short 5 minute video.



Another approach involves "inheriting" the Editor's layout through composition rather than traditional object-oriented inheritance. To do this, the Editor class needs to be a concrete class rather than an abstract class. Here are the necessary changes:
<!-- Editor.mxml -->

<?xml version="1.0" encoding="utf-8"?>
<s:Panel xmlns:fx="http://ns.adobe.com/mxml/2009"
         xmlns:s="library://ns.adobe.com/flex/spark"
         xmlns:mx="library://ns.adobe.com/flex/mx"
         width="500"
         title="{editorType} Editor"
         styleName="editor">

    <fx:Script>
        <![CDATA[
            import mx.events.FlexEvent;

            import spark.components.HGroup;

            public static const REPORT:String="Report";
            public static const LIST:String="List";

            [SkinPart(required="true")]
            public var toolBarChildrenGroup:HGroup;

            [Bindable]
            public var editorType:String;

            public var toolBarChildren:Array;

            override protected function partAdded(partName:String, instance:Object):void
            {
                super.partAdded(partName, instance);

                if (instance == toolBarChildrenGroup)
                {
                    toolBarChildrenGroup.mxmlContent=toolBarChildren;
                }
            }
        ]]>
    </fx:Script>

</s:Panel>
Now, I can compose any class with an instance of the Editor class. For example:
<!-- DocumentEditor.mxml -->

<?xml version="1.0" encoding="utf-8"?>
<s:Group xmlns:fx="http://ns.adobe.com/mxml/2009"
         xmlns:s="library://ns.adobe.com/flex/spark"
         xmlns:mx="library://ns.adobe.com/flex/mx"
         xmlns:comps="comps.*">

    <fx:Script>
        <![CDATA[
            protected function verifyButton_clickHandler(event:MouseEvent):void
            {
                // verify
            }

            protected function formatButton_clickHandler(event:MouseEvent):void
            {
                // format
            }
        ]]>
    </fx:Script>

    <comps:Editor editorType="Document">

        <comps:toolBarChildren>

            <s:Button label="Verify"
                      click="verifyButton_clickHandler(event)"/>

            <s:Button label="Format"
                      click="formatButton_clickHandler(event)"/>

        </comps:toolBarChildren>

        <comps:DocumentForm/>

    </comps:Editor>

</s:Group>

October 20, 2010

Sharing MXML Layout Across Components

Posted by Jeremy Mitchell
Frequently, I find myself creating visual components similar in layout and structure. Unfortunately, unlike properties and methods, layout is a tricky thing to share across components via traditional object-oriented inheritance.

Inevitably, layout code (MXML) gets duplicated across your application. The result is a maintenance nightmare and loads of redundant MXML code. Notice the redundant MXML found in the ReportEditor and ListEditor classes of this application.

Inspired by a colleague of mine, Chris Hayen, this entry will demonstrate code reuse through both inheritance (properties and methods) and composition (layout) without resorting to the undesirable use of ActionScript for layout code.

Composition over Inheritance


In this sample application, I have two editors: a report editor and a list editor. Each share a common layout, therefore, I will extract the layout into its own class - EditorLayout.

Written in MXML, EditorLayout will dictate the layout of each editor. Here's my first pass at EditorLayout.
<!--EditorLayout.mxml-->

<?xml version="1.0" encoding="utf-8"?>
<mx:VBox xmlns:fx="http://ns.adobe.com/mxml/2009" 
         xmlns:s="library://ns.adobe.com/flex/spark" 
         xmlns:mx="library://ns.adobe.com/flex/mx"
         width="100%" height="100%">
    
    <fx:Script>
        <![CDATA[            
            [Bindable]
            public var title:String;
        ]]>
    </fx:Script>
    
    <mx:HBox width="100%"
             backgroundColor="#ededed"
             height="30"
             verticalAlign="middle"
             paddingRight="10"
             paddingLeft="10"
             borderStyle="solid"
             borderColor="#aab3b3">
        
        <mx:HBox id="toolBarChildrenContainer"/>
        
        <mx:Spacer width="100%"/>
        
        <mx:Label id="titleLabel"
                  color="#333333"
                  fontWeight="bold"
                  text="{title}"/>
    </mx:HBox>

    <mx:VBox id="editorChildrenContainer"
            width="100%"
            height="100%"
            minWidth="0"
            minHeight="0"/>

</mx:VBox>
Note: Whenever possible, I always use MXML to define layout. MXML is for layout. ActionScript is for business logic.
Although each editor will have the same layout, their contents will differ. To support custom contents, I'll add a couple of public properties to allow injection of content into the layout. I also need a couple of methods responsible for rendering this content. Here's my second pass at EditorLayout.
<!--EditorLayout.mxml-->

<?xml version="1.0" encoding="utf-8"?>
<mx:VBox xmlns:fx="http://ns.adobe.com/mxml/2009" 
         xmlns:s="library://ns.adobe.com/flex/spark" 
         xmlns:mx="library://ns.adobe.com/flex/mx"
         width="100%" height="100%"
         initialize="onInitialize(event)">
    
    <fx:Script>
        <![CDATA[
            import mx.events.FlexEvent;
            
            [Bindable]
            public var title:String;
            
            private var _toolBarChildren:Array;
            private var _editorChildren:Array;
            
            public function get toolBarChildren():Array
            {
                return _toolBarChildren;
            }
            
            public function set toolBarChildren(value:Array):void
            {
                _toolBarChildren = value;
            }
            
            public function get editorChildren():Array
            {
                return _editorChildren;
            }
            
            public function set editorChildren(value:Array):void
            {
                _editorChildren = value;
            }
            
            protected function onInitialize(event:FlexEvent):void
            {
                createToolBarChildren();
                createEditorChildren();
            }
            
            protected function createToolBarChildren():void
            {
                for each (var toolBarChild:DisplayObject in _toolBarChildren)
                {
                    if (!toolBarChildrenContainer.contains(toolBarChild))
                    {
                        toolBarChildrenContainer.addChild(toolBarChild);
                    }
                }
            }
            
            protected function createEditorChildren():void
            {
                for each (var editorChild:DisplayObject in _editorChildren)
                {
                    if (!editorChildrenContainer.contains(editorChild))
                    {
                        editorChildrenContainer.addChild(editorChild);
                    }
                }
            }
        ]]>
    </fx:Script>
    
    <mx:HBox width="100%"
             backgroundColor="#ededed"
             height="30"
             verticalAlign="middle"
             paddingRight="10"
             paddingLeft="10"
             borderStyle="solid"
             borderColor="#aab3b3">
        
        <mx:HBox id="toolBarChildrenContainer"/>
        
        <mx:Spacer width="100%"/>
        
        <mx:Label id="titleLabel"
                  color="#333333"
                  fontWeight="bold"
                  text="{title}"/>
    </mx:HBox>

    <mx:VBox id="editorChildrenContainer"
            width="100%"
            height="100%"
            minWidth="0"
            minHeight="0"/>

</mx:VBox>
My editors will also share some properties and methods, so I'll create a base class to leverage traditional object-oriented inheritance.
<!--Editor.mxml-->

<?xml version="1.0" encoding="utf-8"?>
<mx:Panel xmlns:fx="http://ns.adobe.com/mxml/2009" 
          xmlns:s="library://ns.adobe.com/flex/spark" 
          xmlns:mx="library://ns.adobe.com/flex/mx" 
          xmlns:comps="comps.*"
          title="{editorType} Editor"
          width="500"
          paddingTop="10"
          paddingBottom="10"
          paddingLeft="10"
          paddingRight="10">
    
    <fx:Script>
        <![CDATA[
            public static const REPORT:String = "Report";
            public static const LIST:String = "List";
            
            [Bindable]
            public var editorType:String;

            protected function save():void
            {
                throw new Error("Abstract Class - override this method in a subclass.");
            }
        ]]>
    </fx:Script>
    
</mx:Panel>
Finally, I'll create my editor classes. An editor class will extend the Editor base class and will be composed of an instance of EditorLayout. Custom contents are injected into the shared layout declaratively using MXML.
<!--ReportEditor.mxml-->

<?xml version="1.0" encoding="utf-8"?>
<comps:Editor xmlns:fx="http://ns.adobe.com/mxml/2009" 
              xmlns:s="library://ns.adobe.com/flex/spark" 
              xmlns:mx="library://ns.adobe.com/flex/mx" 
              xmlns:comps="comps.*" 
              preinitialize="onPreinitialize(event)">
    
    <fx:Script>
        <![CDATA[
            import mx.events.FlexEvent;
            
            protected function saveButton_clickHandler(event:MouseEvent):void
            {
                save();
            }
            
            protected function publishButton_clickHandler(event:MouseEvent):void
            {
                publish();
            }
            
            override protected function save():void
            {
                //save
            }
            
            protected function publish():void
            {
                //publish
            }

            protected function onPreinitialize(event:FlexEvent):void
            {
                editorType = Editor.REPORT;
            }

        ]]>
    </fx:Script>
    
    
    <comps:EditorLayout title="{editorType}">
        
        <comps:toolBarChildren>
            
            <s:Button id="saveButton" 
                      label="Save"
                      click="saveButton_clickHandler(event)"/>
            
            <s:Button id="publishButton" 
                      label="Publish"
                      click="publishButton_clickHandler(event)"/>
            
        </comps:toolBarChildren>
        
        <comps:editorChildren>
            
            <comps:ReportForm id="reportForm"/>
            
        </comps:editorChildren>
        
    </comps:EditorLayout>
    
</comps:Editor>
<!--ListEditor-->

<?xml version="1.0" encoding="utf-8"?>
<comps:Editor xmlns:fx="http://ns.adobe.com/mxml/2009" 
              xmlns:s="library://ns.adobe.com/flex/spark" 
              xmlns:mx="library://ns.adobe.com/flex/mx" 
              xmlns:comps="comps.*" 
              preinitialize="onPreinitialize(event)">
    
    <fx:Script>
        <![CDATA[
            import mx.events.FlexEvent;
            
            protected function saveButton_clickHandler(event:MouseEvent):void
            {
                save();
            }
            
            protected function submitButton_clickHandler(event:MouseEvent):void
            {
                submit();
            }
            
            override protected function save():void
            {
                //save
            }
            
            protected function submit():void
            {
                //submit
            }
            
            protected function onPreinitialize(event:FlexEvent):void
            {
                editorType = Editor.LIST;
            }
        ]]>
    </fx:Script>
    
    
    <comps:EditorLayout title="{editorType}">
        
        <comps:toolBarChildren>
            
            <s:Button id="saveButton" 
                      label="Save"
                      click="saveButton_clickHandler(event)"/>
            
            <s:Button id="submitButton" 
                      label="Submit"
                      click="submitButton_clickHandler(event)"/>
            
        </comps:toolBarChildren>
        
        <comps:editorChildren>
            
            <comps:ListForm id="listForm"/>
            
        </comps:editorChildren>
        
    </comps:EditorLayout>
    
</comps:Editor>
The final result (see below) is cleaner code throughout your application (no ActionScript layout code and no redundant MXML code).



View source is enabled

Of course, a better technique involves the use of Flex 4's skinning architecture. Stay tuned.

Update: See blog entry entitled "Flex Skinning to Share Common Layout".

September 24, 2010

Temporarily Disable a Flex Component

Posted by Jeremy Mitchell
On occassion, you may find the need to temporarily prevent user interaction with a Flex component. A common example includes disabling a form while data is retrieved from the server via HTTPService, WebService or RemoteObject.

The following example demonstrates this functionality minus the specifics of a real world implementation.



View Source is enabled

To replicate this functionality in your own application, use the Flex classes provided in the SWF and do the following:
  1. Register a container with a unique identifier to be temporarily disabled on request
  2. Initiate a request to overlay the container with a UIComponent to "disable" the container
  3. Initiate a request to remove the container's overlay to "re-enable" the container

Register a container with a unique identifier to be temporarily disabled on request


For each container (i.e. VBox, HBox, Form, etc) requiring the need to be temporarily disabled, create an entry in a global registry. This entry will consist of the container and a identifier required to uniquely identify the container. This entry should be part of the container's class definition and should be created as soon as possible in the component's lifecycle. For example, if the container is an ActionScript component, create the entry in the constructor. If the container is an MXML component, a preinitialize event handler should suffice.
private var _progressRegionId:String = [find something unique];

protected function onPreinitialize(event:FlexEvent):void
{
    var progressRegion:ProgressRegion = new ProgressRegion(_progressRegionId, [container to disable]);
    ProgressManager.addProgressRegion(progressRegion);
}

Initiate a request to overlay the container with a UIComponent to "disable" the container


This requires nothing more than a call to the static method ProgressManager.showModal() with a reference to the container you'd like to temporarily disable. The container's reference is the unique identifier chosen during registration of the container. When performing a service call via HTTPService, WebService or RemoteObject, you may want to initiate this action.
ProgressManager.showModal(_progressRegionId);

Initiate a request to remove the container's overlay to "re-enable" the container


Again, using the classes provided, this is pretty simple. Just make a call to another static method, ProgressManager.hideModal(), along with a reference to the container you'd like to re-enable. A great place to do this may be in your service call's result and/or fault handler.
ProgressManager.hideModal(_progressRegionId);
Note: Currently, this modal overlay only works with "containers" that derive from the Container class. I.e. HBox, VBox, etc.
Hope you find this useful.

August 19, 2010

Developing Flash and Flex applications for Android

Posted by Derrick Grigg

Since Apple kiboshed Adobe's hopes of iPhones running Flash content, Android (Google) has stepped up and offered Flash and Flex developers a mobile platform to development and deliver content on. This has come as great news to Flash platform developers who want the opportunity to dive into the mobile environment. Many Flash (and Flex) developers I have spoken to about mobile development are still unsure about what it will take to deliver Flash player based content and applications to mobile devices.

The good news is the development and delivery process is almost identical to the current process. Use Flash or Flash Builder to create a swf file and then either deploy that to a website, to be served up via an HTML page or wrap it with the AIR runtime and packge it for installation on an Android phone as an native Android installer. Really the only difference in developing for the Android platform is the extra step required to create an '.apk' file if you want to create a stand alone application.

The bad news is the environment that the content and applications are running in is quite different from anything the vast majority of Flash platform developers have worked in. The screen is small, the bulk of user interactions are touch based, the screen can easily be rotated and tilted, the user is often quickly looking at the screen while interacting with the environment around them (ie texting and walking at the same time) and the processor doesn't have the juice most modern desktops and laptops have. This means that while the swf delivery and development process is quite familar the UI and UX development is anything but. Add in the processing constraints and most Flash platform developers will quickly realize there is a significant learning curve to developing Flash and Flex content for Android (and mobile in general) devices.

So what are the key areas developers need to be aware of?

  1. Screen size
  2. User interaction
  3. Device capabilities
  4. Performance

1. Size, it really does matter

Below are two screen shots of the same Flash application running on a Google Nexus One and a normal desktop computer. The application is 480 x 800. The first image shows the screen as seen on a normal desktop computer with the screen resolution set at 1280 x 1024 (fairly standard desktop resolution). The second image shows the same screen scaled to the physical size as seen on the Nexus screen. It might seem obvious that the mobile screen will be smaller than the desktop screen but watch how screen resolution affects something that is the "same" size.

Desktop screen shot

Flex Android app on desktop

Android (Nexus One) screen shot

As you can see there is a huge difference in the physical size of the two screens even though the application is viewed at 480 x 800 pixels on both devices. Mobile devices have much higher screen resolutions than normal computers, 240dpi vs 72dpi. As you can see from the images above this makes a huge difference.

For mobile applications this has two very big implications.

First a smaller screen with a much higher resolution means you have much less physical real estate to work with when laying out the user interface. Designers need to be acutely aware of this when creating screens and views. Add in the fact the users are often quickly glancing at the screen which means screens can not afford any clutter and designers have a difficult task in packing a lot of meaningful content into a very small area.

Second, since everything appears much smaller on mobile screens things like text and UI controls (button, checkboxes, etc) need to made larger for readability and clickability (if a beer company can use drinkability I can use clickability). On a mobile device the main pointer is the finger tip which is a lot larger and less precise than a mouse pointer. In a desktop application the typical button size is around 25 pixels in height, on a mobile device 60 pixels is the norm, anything smaller than that and it becomes very challenging to click the button you want.

I couldn't help but be reminded of this Simpson's clip after my first experiments building a Flash application for the Android.

This leads to my second point.

2. User interaction, it's all about the finger

Apple and the iPhone ushered in a whole new era of computer interaction. Sure 'touch' has been around for a long time, but it was Apple who made it mainstream. Point and click are things of the past, now it's point, flip, swipe, pinch, wipe. Sounds more like a heated game of rock-paper-scissors. The great news for Flash platform developers is that AS3 has full gesture support built in and ready to be used. Events are dispatched for a multitude of user gestures including tapping, swiping and rotating. AS3 also supports multi touch for instances where multiple touch points need to be tracked.

This is a whole new territory for Flash and Flex developers, we generally just interact with the mouse and keyboard, a few of us (myself included) have developed for larger touch screens, but not small mobile screens. Fortunately there is some good information to be found on designing for human interaction. Google has provided some guidelines for user interface design and Apple has some really good (and thorough) documentation on Human Interface design.

This is an area that will make or break an application. It won't matter how great the apps processes information or what information it displays or what it allows users to do, if the control is non-intutive and confusing users will drop it faster than a hot potato (and they may just throw their phone).

3. Device Capabilities, the swiss army knives of personal computing

Most mobile devices have as many sensors and media inputs as swiss army knives have blades. There are GPS sensors, accelerometers, touch screens, cameras, microphones, video cameras, scroll balls, the list goes on. The trick for developers is figuring out how to leverage the various sensor inputs (and outputs) to create unique and pleasant user experiences.

For the Flash and Flex developer most (if not all) of these sensors will be available to AIR based applications and most of them will be available to browser based swfs. This an area where Flash applications will really be able to shine. Flash has supported camera and video input for a long time, working with these types of media inputs should be second nature to many Flash platform developers. Some of the other sensor inputs are new (ie accelerometer, microphone and gps) but Flash has the advantage of being able to access and use them, sorry HTML5 and javascript.

Personally I think Flash is uniquely positioned to offer mobile users amazing experiences based on the capabilities of the devices. Flash has been used for years to create amazing, immersive, media rich user experiences. With increased ways to receive and provide feedback between the user and device who knows what applications will start to look like and how they will function.

4. Performance, where the rubber meets the road

Performance, performance, performance. This is "allegedly" why Apple doesn't offer Flash on the iPhone/iPad. I don't buy it. I have seen Flash running on Android devices first hand, both in the browser and as stand alone applications and trust me it runs perfectly. Adobe has worked very closely with mobile manufacturers to provide hardware acceleration, reduce battery usage and deliver maximum performance with the upcoming Flash 10.1 release.

The critical issue for developers is to remember they are developing for devices that don't have the same processing resources of typical desktop and laptop computers. Special attention needs to be paid to optimizing performance as much as possible. Grant Skinner has a great presentation on Flash performance, where gains can be found and where trouble lurks. Garbage collection, green threading, blitting and generics are all areas that AS3 developers should understand in order to optimize and maximize the performance Flash can deliver.

The old computer mantra is "garbage in ...garbage out". If your application runs slow, freezes or crashes, don't blame the device, virtual machine, Adobe, Google, Android or your mother, first look at your code. This is where the majority of backlash against Flash performance should really be pointed. It's not the Flash plugin and runtime, it's the fact that anyone can "develop" a Flash application and many people that do, don't understand the performance and memory implications of what they are doing. With the ability to release Flash applications and content to mobile devices, Flash will be under increased scrutiny, do the community a favor and write clean, efficient code.

Where to next?

Who knows? The mobile environment is like the internet was 10-15 years ago, the wild wild west. A lot of really cool things will get made and a lot of dogs will come and go. The only thing I am sure of is that the Flash platform will have a large place in how applications and content get developed, deployed and used. It's an exciting time for Flash platform developers, especially after all the HTML5/Apple rhetoric. Get a mobile device, get some neat ideas or find someone who has them and build something amazing, I know I will be.




Derrick Grigg is a freelance web contractor who specializes in developing Flex and Flash applications. He can be found at dgrigg.com or followed on Twitter


July 15, 2010

Flex DateChooser with Selectable Week / Month

Posted by Jeremy Mitchell
Recently, I was given the task of enhancing Flex's DateChooser to include the ability to select a week or a month. Currently, you can select a week or a month by highlighting multiple days using shift-click (if allowMultipleSelection = true), but we wanted to make it a little easier on the user.

Here is the final product (click on an arrow or the month name):



View Source is enabled


Implementation of this functionality included the following:
  1. Creating a class to represent the "week selection arrow"
  2. Extension of the CalendarLayout class
  3. Creation of a custom event to indicate when a week was selected and help us determine which week was selected
  4. Extension of the DateChooser class
  5. Creation of some utility methods to help us determine the start / end date of a selected week or month

In addition to providing the source code, this blog entry documents the implementation details and provides a brief explanation of the applied logic.


Creating a class to represent the "week selection arrow"


Let's start with creating the arrow used for selecting a week. This class will be primarily responsible for drawing an arrow. However, it will also hold a reference to the arrow's row number (this is important when determining which calendar days to programatically select when the arrow is clicked).
package components
{
    import flash.events.MouseEvent;
    import flash.geom.Point;

    import mx.core.UIComponent;

    public class WeekSelector extends UIComponent
    {
        public var weekRow:int;

        public function WeekSelector(weekRow:int)
        {
            super();
            this.weekRow = weekRow;
            height = 5;
            width = 5;
            buttonMode = true;
            useHandCursor = true;
            addEventListener(MouseEvent.MOUSE_UP, onMouseUp)
        }

        private function onMouseUp(event:MouseEvent):void
        {
            // we don't want to interfere with CalendarLayout's MOUSE_UP handler
            event.stopImmediatePropagation();
        }

        override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
        {
            var vertices:Vector.<Number> = new Vector.<Number>;

            var top:Point = new Point(0, 0);
            var bottom:Point = new Point(0, height);
            var right:Point = new Point(width, height / 2);

            vertices.push(top.x, top.y, bottom.x, bottom.y, right.x, right.y);

            graphics.beginFill(0x000000, 1);
            graphics.drawTriangles(vertices);
            graphics.endFill();
        }
    }
}

Extension of the CalendarLayout class


Our new DateChooser's layout includes arrows for selecting a week. Flex's DateChooser delegates layout of its calendar to an instance of the CalendarLayout class (see DateChooser.dateGrid). Therefore, we will need to extend CalendarLayout and do the following:

  1. Create an instance of WeekSelector (the arrow) for each calendar row
  2. Position each arrow to the left of its corresponding calendar row
  3. Register click handlers on each arrow

Simple enough, right? Sure, but there's a slight snag. Positioning the arrows requires knowledge of each calendar row's position (the x & y values of the row). This information can be extracted from the CalendarLayout's dayBlocksArray property which is marked mx_internal. Fortunately, unlike private variables, mx_internal properties are accessible to subclasses so I simply assigned a property of my subclass to the CalendarLayout's dayBlocksArray property. Now I can access the contents of dayBlocksArray.
package components
{
    import events.WeekSelectionEvent;
    
    import flash.events.MouseEvent;
    import flash.geom.Point;
    
    import mx.controls.CalendarLayout;
    import mx.core.UITextField;
    import mx.core.mx_internal;

    public class CustomCalendarLayout extends CalendarLayout
    {
        private const ROWS_IN_CALENDAR_LAYOUT:int = 5;
        private var weekSelectors:Array = [];
        private var dayBlocksArray:Array = [];

        public function CustomCalendarLayout()
        {
            super();
        }

        override protected function createChildren():void
        {
            super.createChildren();
            createWeekSelectors();
        }

        override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
        {
            super.updateDisplayList(unscaledWidth, unscaledHeight);
            // we need to get a hold of a reference to the superclass' dayBlocksArray property
            // it contains information needed to position each week selection arrow
            dayBlocksArray = mx_internal::dayBlocksArray 
            positionWeekSelectors();
        }

        private function createWeekSelectors():void
        {
            // row zero is [S, M, T, W, T, F, S], we don't need an arrow for that row
            // so let's start with row one
            var weekRowIndex:int = 1; 

            while (weekRowIndex <= ROWS_IN_CALENDAR_LAYOUT)
            {
                // create an arrow for each row
                weekSelectors.push(new WeekSelector(weekRowIndex));
                addChild(weekSelectors[weekRowIndex - 1]);
                weekSelectors[weekRowIndex - 1].addEventListener(MouseEvent.CLICK, onClick);
                weekRowIndex++;
            }
        }

        private function positionWeekSelectors():void
        {
            var weekRowPosition:Point;
            var weekSelector:WeekSelector;
            // again, let's start with row one (no need for the [S, M, T, W, T, F, S] row)
            var weekRowIndex:int = 1;

            while (weekRowIndex <= ROWS_IN_CALENDAR_LAYOUT)
            {
                weekRowPosition = getWeekRowPosition(weekRowIndex);
                weekSelector = weekSelectors[weekRowIndex - 1];
                weekSelector.x = weekRowPosition.x;
                weekSelector.y = weekRowPosition.y;
                setChildIndex(weekSelector, numChildren - 1);
                weekRowIndex++;
            }
        }

        private function getWeekRowPosition(row:int = 0):Point
        {
            // using dayBlocksArray, we can find a UITextField that represents the last day (column 6) in the row
            // and we can use that UITextField's X & Y properties to determine the positioning of the row
            // and ultimately where to position the arrow
            var column:int = 6;
            var day:UITextField = dayBlocksArray[column][row];
            var rowY:Number = day.y + day.height / 3;

            return new Point(3, rowY);
        }

        private function onClick(event:MouseEvent):void
        {
            var dayInWeek:int = getDayInSelectedWeek(event);
            dispatchWeekSelectionEvent(dayInWeek);
        }

        private function getDayInSelectedWeek(event:MouseEvent):int
        {
            var target:WeekSelector = event.target as WeekSelector; // target is the arrow that was clicked
            var row:int = target.weekRow; // the arrow knows which row it belongs to
            var day:UITextField; // the dateChooser calendar is a grid of UITextFields

            const FIRST_DAY_OF_MONTH:int = 1;
            
            // let's loop through each column in the arrow's row until we find a UITextField with text
            // this means - until we find a date. notice how the first row in a calendar sometimes 
            // doesn't have a date at the beginning of the week?

            for (var columnIndex:* in dayBlocksArray)
            {
                day = (dayBlocksArray[columnIndex][row] as UITextField)

                if (day.text && day.text != "")
                {
                    return parseInt(day.text);
                }
            }

            return FIRST_DAY_OF_MONTH;
        }

        private function dispatchWeekSelectionEvent(dayInWeek:int):void
        {
            var weekSelectionEvent:WeekSelectionEvent = new WeekSelectionEvent(WeekSelectionEvent.WEEK_SELECTED, true);
            weekSelectionEvent.dayInWeek = dayInWeek;
            // dispatch the bubbling event that will get caught by our custom datechooser
            // and select all the days for that week
            dispatchEvent(weekSelectionEvent);
        }
    } //EOClass
} //EOPackage

Creation of a custom event to indicate when a week was selected and help us determine which week was selected


There's nothing particularly interesting about this task. We need a custom event that bubbles to indicate when a week has been selected. This event's payload includes a day (any day really) that is part of the selected week. We'll use that day to figure out the start and end dates of the selected week.

If you need a refresher on creating custom events read Flex Examples - Creating a Custom Event.
package events
{
    import flash.events.Event;

    public class WeekSelectionEvent extends Event
    {
        public static const WEEK_SELECTED:String = "weekSelected";
        
        protected var _dayInWeek:int;

        public function WeekSelectionEvent(type:String, bubbles:Boolean = false, cancelable:Boolean = false)
        {
            super(type, bubbles, cancelable);
        }
        
        override public function clone():Event
        {
            var clone:WeekSelectionEvent = new WeekSelectionEvent(type, bubbles, cancelable);
            clone.dayInWeek = dayInWeek;
            return clone;
        }
        
        public function get dayInWeek():int
        {
            return _dayInWeek;
        }

        public function set dayInWeek(value:int):void
        {
            _dayInWeek = value;
        }
    } //EOClass
} //EOPackage

Extension of the DateChooser class


It's time to bring this all together. We'll need a modified version of DateChooser to utilize our custom calendar layout (CustomCalendarLayout) that includes our nifty little selection arrows. In addition, we'll add the ability to select the entire month by simply clicking on the month label.

Again, we're fortunate that the DateChooser properties needed to complete this undertaking (dateGrid and monthDisplay) are accessible via mx_internal. Otherwise, we'd be in a world of hurt.
package components
{    
    import events.WeekSelectionEvent;
    
    import flash.events.MouseEvent;
    
    import mx.controls.*;
    import mx.core.UITextField;
    import mx.core.UITextFormat;
    import mx.core.mx_internal;
    import mx.events.CalendarLayoutChangeEvent;
    import mx.events.DateChooserEvent;
    import mx.styles.StyleProxy;
    
    import utils.CustomDateChooserUtil;

    public class CustomDateChooser extends DateChooser
    {
        private const FIRST_DAY_OF_MONTH:int = 1;
        
        private var dateGrid:CalendarLayout;

        public function CustomDateChooser()
        {
            super();
            // dispatched from our CustomCalendarLayout when a "week arrow" is clicked
            addEventListener(WeekSelectionEvent.WEEK_SELECTED, selectWeek); 
        }

        override protected function createChildren():void
        {
            overrideAddDateGrid_in_superclass_createChildren();
            super.createChildren();
            addMonthDisplayHandlers();
            addDateGrid();
        }

        private function addMonthDisplayHandlers():void
        {
            // make the date chooser's month display function like a link
            mx_internal::monthDisplay.addEventListener(MouseEvent.CLICK, selectMonth);
            mx_internal::monthDisplay.addEventListener(MouseEvent.MOUSE_OVER, onMouseOverMonthDisplay);
            mx_internal::monthDisplay.addEventListener(MouseEvent.MOUSE_OUT, onMouseOutMonthDisplay);
        }

        private function addDateGrid():void
        {
            addChild(mx_internal::dateGrid);
        }

        private function selectWeek(event:WeekSelectionEvent):void
        {
            // catch the event dispatched when a week selection arrow is clicked
            // and set the DateChooser's selectedRanges property to a value representing the selected week
            var dayInWeek:int = event.dayInWeek as int;
            var selectedDate:Date = new Date(displayedYear, displayedMonth, dayInWeek);
            var weekRange:Object = CustomDateChooserUtil.getWeekOf(selectedDate);
            
            selectedRanges = [weekRange];
        }

        private function selectMonth(event:MouseEvent):void
        {
            // set the DateChooser's selectedRanges property to a value representing the entire displayed month
            var selectedDate:Date = new Date(displayedYear, displayedMonth, FIRST_DAY_OF_MONTH);
            var monthRange:Object = CustomDateChooserUtil.getMonthOf(selectedDate);
            
            selectedRanges = [monthRange];
        }

        private function onMouseOverMonthDisplay(event:MouseEvent):void
        {
            highlightMonthDisplay(event);
        }

        private function onMouseOutMonthDisplay(event:MouseEvent):void
        {
            unHighlightMonthDisplay(event);
        }

        private function highlightMonthDisplay(event:MouseEvent):void
        {
            // bold / underline on rollover (just like a link)
            var monthDisplay:UITextField = event.target as UITextField;
            var textFormat:UITextFormat = new UITextFormat(this.systemManager);
            textFormat.underline = true;
            textFormat.color = 0x0000FF;
            monthDisplay.setTextFormat(textFormat);
        }

        private function unHighlightMonthDisplay(event:MouseEvent):void
        {
            // unbold / remove underline on rollout (just like a link)
            var monthDisplay:UITextField = event.target as UITextField;
            var textFormat:UITextFormat = new UITextFormat(this.systemManager);
            textFormat.underline = false;
            textFormat.color = 0x000000;
            monthDisplay.setTextFormat(textFormat);
        }

        private function overrideAddDateGrid_in_superclass_createChildren():void
        {
            // we're going to assign the DateChooser's dateGrid property an instance of our CustomCalendarLayout
            // fortunately, super.createChildren will not override our assignment. our CustomCalendarLayout is in place.
            dateGrid = new CustomCalendarLayout(); // this is really the only line I care about changing
            // the rest of this method works exactly as the super.createChildren method does
            dateGrid.styleName = new StyleProxy(this, calendarLayoutStyleFilters);
            // however, unfortunately the event handlers in the super class are marked private
            // so i'll have to copy their contents from the super class into this subclass
            dateGrid.addEventListener(CalendarLayoutChangeEvent.CHANGE, copyOf_DateGrid_changeHandler_from_superclass);
            dateGrid.addEventListener(DateChooserEvent.SCROLL, copyOfDateGrid_scrollHandler_from_superclass);
            mx_internal::dateGrid = dateGrid;
        }

        private function copyOf_DateGrid_changeHandler_from_superclass(event:CalendarLayoutChangeEvent):void
        {
            // this is just a copy of the DateChooser.dateGrid_changeHandler 
            // but, i had to use the selectedDate public setter instead of the private variable
            selectedDate = CalendarLayout(event.target).selectedDate; 
            
            var e:CalendarLayoutChangeEvent = new CalendarLayoutChangeEvent(CalendarLayoutChangeEvent.CHANGE);
            e.newDate = event.newDate;
            e.triggerEvent = event.triggerEvent;
            dispatchEvent(e);
        }

        private function copyOfDateGrid_scrollHandler_from_superclass(event:DateChooserEvent):void
        {
            // this is just a copy of the DateChooser.dateGrid_scrollHandler
            dispatchEvent(event);
        }

    } //EOClass
} //EOPackage

Creation of some utility methods to help us determine the start / end date of a selected week or month


You'll notice in CustomDateChooser we used two methods to help us determine the start and end date of the selected week (getWeekOf) or the start and end date of the selected month (getMonthOf). Here is the implementation of those methods.
package utils
{
    public class CustomDateChooserUtil
    {
        public static function getWeekOf(selectedDate:Date):Object
        {
            // takes in a date and returns an object representing a start and end date for the selected week
            // in a format that DateChooser.selectedRanges likes
            var weekIndex:int = selectedDate.day;
            var weekStartDate:Date = new Date(selectedDate.fullYear, selectedDate.month, selectedDate.date - weekIndex);
            var weekEndDate:Date = new Date(weekStartDate.fullYear, weekStartDate.month, weekStartDate.date + 6);

            return {rangeStart: weekStartDate, rangeEnd: weekEndDate};
        }

        public static function getMonthOf(selectedDate:Date):Object
        {
            // takes in a date and returns an object representing a start and end date for the selected month
            // in a format that DateChooser.selectedRanges likes
            var monthStartDate:Date = new Date(selectedDate.fullYear, selectedDate.month, 1);

            // Flex interprets day 0 to be the last day of the preceeding month
            var monthEndDate:Date = new Date(selectedDate.fullYear, selectedDate.month + 1, 0);

            return {rangeStart: monthStartDate, rangeEnd: monthEndDate};
        }
    } //EOClass
} //EOPackage
There you go. I hope people will find this component useful. Credit must also be given to my colleague, Paul Saieg, as we "paired" on the development of this component.

June 25, 2010

Event Propagation in Flex/ActionScript 3

Posted by Jeff Krueger
I came across a question in an interview the other day about event propagation and I thought it would be a good topic to blog about. Most of the time you never need to know the details of event propagation in Flex. All you normally do is setup your event listener inline on the component.

<mx:Button id="myButton2" label="test" click="myButton_click(event)"/> 
or by adding it explicitly to the component via ActionScript.

myButton.addEventListener(MouseEvent.Click, myButton_click);
Then you put your code in your eventListener function and you are done. But what is truly going on when the user clicks the button? Event propagation is made up of 3 parts.
  • Capturing
  • Targeting
  • Bubbling
Capturing and Bubbling are similar but work in opposite directions. When a user clicks on a button the capturing phase begins working its way down the DisplayList looking for items listening for the given event type with the useCapture option set to true. By default, useCapture is set to false. When it is set to false, the eventListener will be called during the bubbling phase. To add an eventListener for the capture phase you have to use the second method mentioned above passing in true for the third parameter. For example:

myButton.addEventListener(MouseEvent.CLICK, myButton_click, true)
When the capturing phase hits the point where the object from the displayList being evaluated is the one that initiated the event, it changes to the Targeting phase. This is known because the target and currentTarget variables will equal. This is explained more later. Once this is reached then the BubblingPhase begins for events that support bubbling.

During the BubblingPhase the displayList is walked in reverse and the system is looking for eventListeners for this event with the useCapture set to false (the default value). When they are found, those eventListeners are executed.

During both Capturing and Bubbling the event object has two variables that are important. One is target and the other is currentTarget. Target is always the object that initiated the event. The currentTarget is always the current item in the displayList being processed. You should always use currentTarget in your eventListener functions. That will always be the object that the eventListener is associated to. For example, if the user clicks on the text of the button. The target will be set to a label class since that is the sub component that was truly clicked on and initiated the event process. But when your eventListener function, attached to the button, is executed the currentTarget will be the button and the target will still be the label.

At any point in the Capturing or Bubbling process you can stop the process from continuing to the next item in the display list. There are two options, stopPropagation and stopImmediatePropagation. The difference between the two functions is that stopPropagation will allow other items at the same level in the displayList to receive the event, where the stopImmediatePropagation function stops right then.

Understanding the event propagation within Flex provides many options. You can control what components get to see events. You can change the behavior of items, mostly done when you are building custom components. You can reduce code by putting shared code in a common click function at a higher level. And, of course, the more you understand about how components interact the better custom components you can build.

April 11, 2010

10 Things A Good Flex Developer Should Know

Posted by Jeremy Mitchell
It takes more to be a good Flex programmer than simply knowing how to use some built-in Flex containers and controls. It takes significantly more.

Here's my take on the subject...along with some resources or some keywords that you can easily Google.

Please comment on this blog if you feel like I've overlooked anything (which is inevitable) or if you know of some valuable resources that I should add.

1. Object-oriented programming (OOP)

The language on which Flex is based, ActionScript 3, is a fully object-oriented language. Although not an easy concept to grasp, object-oriented programming is a prerequisite to learning Flex. If your prior experience includes OOP (Java, C#, Ruby, etc), then you're set. If not, you'll need to pick up an OOP book and start learning ASAP.
Note: Some of you may be asking - "What about design patterns?". Let's take one step at a time, shall we? Concern yourself with understanding classes and objects, interfaces, inheritance, composition, polymorphism, encapsulation, etc. Only then should you even consider looking at design patterns. In fact, if I write a post entitled "10 Things A GREAT Flex Developer Should Know", design patterns will be on that list.

2. ActionScript / MXML

ActionScript is the programming language used along with MXML to create Flex applications. MXML is tag-based declarative language written in XML. Each MXML tag is mapped directly to a corresponding ActionScript class. MXML is primarily used by Flex developers to lay out the UI, whereas, ActionScript is used for business logic. Exceptions apply, of course.

The Flex Framework includes hundreds of ActionScript classes and interfaces used to develop Flex applications. Your skill level as a Flex developer is tied directly to your proficiency with ActionScript and MXML.
Note: Get comfortable with the Flex Language Reference. As a Flex developer, you will use it on a daily basis.

3. Debugging

A large portion of any developer's time is spent debugging. Obviously, debugging is required to track down the root cause of bugs. However, debugging is also a great way to learn a new code base.

Fortunately, there are many tools available to assist you with your debugging efforts. Invest some time in learning these tools. Your investment will pay dividends immediately.

4. Event-driven programming

Flex applications are event-driven. Every action is the result of an asynchronous event.

As a Flex developer, you must know how to respond to events and how to create and dispatch events. To accomplish this, a solid understanding of Flex's event architecture is required including familiarity with the following concepts:
  • Built-in events (Flash Player or Flex Framework events)
  • Custom events (Events created by the developer that extend the Event class or one of its subclasses)
  • Event dispatchers, event broadcasters (See EventDispatcher class and its dispatchEvent method)
  • Event listeners, event handlers (See EventDispatcher class and its addEventListener & removeEventListener methods)
  • Event Flow (capture, target & bubbling phases; target vs. currentTarget)
  • Event objects, event types (See Event class and subclasses)
  • Event default behavior (See Event class and subclasses and its preventDefault method)

5. Data binding

On the surface, data binding is a no brainer. Bind the value of one property to the value of another property using curly bracket notation. When the value of the source property changes, so does the value of the destination property.

However, there is overhead associated with data binding and indiscriminate use can have performance implications. A solid understanding of data binding will help you determine when it is appropriate and when it is not.

6. Item renderers

One characteristic of a well-designed Flex application is the presentation of data in a visually-compelling manner. Flex provides a number of list-based controls (DataGrid, List, TileList, HorizontalList, etc) responsible for the presentation of data. However, only with the help of an item renderer can a list-based control customize the display of the data.

You will spend a lot of time working with item renderers. Get very comfortable with how they work.

7. Accessing remote data

Do you know of many applications that don't interact with data? Me neither. Learn how to retrieve data via HTTPService, WebService and RemoteObject. A Flex architecture framework may also help you with this (see #9).

8. Styling / Skinning

Let's not forget that Flex is a UI technology and as such certain design expectations exist. As a Flex developer, you should be able to customize the look and feel of your Flex applications using CSS styling and/or graphical or programmatic skins.

With Flex 4, there are no more excuses. Spend a little time getting to know the right side of your brain for once. It's a nice change of pace, and it will help differentiate yourself from other Flex developers.

9. At least one Flex architecture framework

Most Flex architecture frameworks enforce a separation of concerns by implementing an MVC (model-view-controller) application design. In addition, many of these same frameworks dictate how your code should be organized and packaged within your Flex project.

Although many would argue that frameworks are unnecessary, I believe that Flex developers benefit in many ways from the experience of using one. Simply witnessing the techniques (good or bad) employed by a framework to solve complex architectural issues contributes to your growth as a Flex developer / architect.

Also, it's hard to deny the fact that framework experience will substantially increase your marketability as a Flex developer. Jesse Warden recently told me "There are a few shops that don't use frameworks, but those are rare. It's in 'fashion' whether we like it or not." I agree with Jesse.See Finding the Most Popular Flex Framework

10. Component lifecycle & the display list

I wasn't convinced of the need to learn the Flex component lifecycle or the display list until I wrote my first "custom" component (actually, it was a semi-custom component that extended Canvas). Until that time, I used built-in Flex components and blissfully lived in MXML land where the display list was handled for me. Never once did I encounter an addChild, createChildren or commitProperties method, and I used the creationComplete event for EVERYTHING.

My first custom component utilized a handful of asynchronous events, and I could not reliably predict the order in which each event would be handled. Only after I learned of the Flex component lifecycle methods and dirty flags could I regain control.

These lifecycle methods are already there. Learn how they work and use them to your benefit. Your life will be easier and you'll lose less hair.

February 22, 2010

Binding a Value to a ComboBox

Posted by Wade
When working with the ComboBox component, I have found that I pretty much always want to bind a value to the ComboBox so that it automatically selects a corresponding record in the dataProvider. The most straight forward example of this is with a ComboBox on an address form to select a state. If you load an address into the form, the ComboBox should automatically select the state associated to that address.

To accomplish this I followed a post by Ben Forta to extend the ComboBox, and have made some changes over the years to fine tune it. The principle idea of the approach is to add a selectedValue property to the ComboBox and then loop through the dataProvider to look for a match whenever the selectedValue or the dataProvider is changed. I added dataProperty for specifying the property in the dataProvider that the value should map to, and an additional override to the collectionChangeHandler to catch any updates to the dataProvider.

I settled on naming the component VBComboBox, with VB standing for Value Bound. I have it implemented as an actionscript class in my projects, but wrapped in mxml for this blog post to avoid having to specify a package name. I've included an example of the component at the bottom of this post with viewable source, so I'll just walk though some of the highlights.

All the "magic" happens in the commitProperties method. This method is executed anytime the properties of the class are invalidated. The setter for the dataProvider in the ComboBox calls invaidateProperties() as does the setter for the selectedValue in VBComboBox. Each setter also sets a flag that the value has changed. The following is a rough breakdown of the functionality that was added.
  • The first if statement in the commitProperties method checks these flags to see if the dataProvider or selectedValue have changed.
  • If a change is detected, then loop through all the objects in the dataProvider.
  • The if statement in the loop checks the dataProperty (defaulted to "data") of the current object against the selectedValue. If they match the idx var is set with the current position and the loop is broken.
  • The selectedIndex is set to the position that was found or -1. Setting it to -1 if no match is found will cause the prompt value assigned to the ComboBox to be displayed.
override protected function commitProperties():void
{
    super.commitProperties();

    //If the selectedValue and dataProvider are set, find the selectedIndex of the value.
    if ((selectedValueChanged == true && dataProvider != null) ||
        (dataProviderChanged == true && selectedValue != undefined))
    {
        dataProviderChanged=false;
        selectedValueChanged=false;

        var idx:int=-1;

        //Loop through data provider until a record with the value is found.
        for (var i:int=0; i < dataProvider.length; i++)
        {
            if (this.dataProvider[i] != null &&
                this.dataProvider[i].hasOwnProperty(dataProperty) &&
                this.dataProvider[i][dataProperty] == selectedValue)
            {
                idx=i;
                break;
            }
        }

        //Set the selectedIndex with the index found or -1 if not found.
        this.selectedIndex=idx;
    }
}
There is also an override of the collectionChangeHandler to catch changes to the dataProvider that happen outside of the setter. This can happen records are added, removed, or if the source of an ArrayCollection is reset for example. In this case, the dataProviderChanged flag is set to true, and the properties are invalidated to re-check the selectedValue against the dataProvider.
override protected function collectionChangeHandler(event:Event):void
{
    super.collectionChangeHandler(event);

    dataProviderChanged=true;
    invalidateProperties();
}
To view the entire source, right click on the example and select "View Source".



January 28, 2010

Local Shared Object (Flash Cookie)

Posted by Jeremy Mitchell
From Wikipedia, a browser cookie is a "small piece of text stored on a user's computer by a web browser. A cookie consists of one or more name-value pairs containing bits of information".

A web application uses cookies for session management, personalization and tracking. Most browsers restrict the size of a cookie to 4 kb.

In a Flex application, storing data on a user's computer for later use is accomplished using Local Shared Objects rather than cookies. A Local Shared Object can store up to 100 kb of data without asking permission of the user. Unlike cookies that are capable of storing only text values, Local Shared Objects can store many data types including Number, String, Boolean, XML, Date, Array & Object. Instances of a custom class can also be stored in a Local Shared Object when the custom class is registered using the [RemoteClass] metadata tag.

Create / retrieve a Local Shared Object

The process for creating a new Local Shared Object or retrieving an existing Local Shared Object is identical. In both cases, the static method getLocal() is used.
public static function getLocal(name:String, localPath:String = null, secure:Boolean = false):SharedObject
First, perform a call to the static method getLocal() of the SharedObject class, and then save the reference in a variable.
var myNewLocalSharedObject:SharedObject = SharedObject.getLocal("myNewLocalSharedObject");
Flash Player will look in a local directory on the user's computer for a Local Shared Object named myNewLocalSharedObject. More specifically, it will look in a directory unique to the SWF's domain for a file named myNewLocalSharedObject.sol. If myNewLocalSharedObject.sol is found, a reference to the existing Local Shared Object is returned. Otherwise, myNewLocalSharedObject.sol is created and a reference is returned to the newly created Local Shared Object.

To determine whether a new or existing Local Shared Object was returned, query the size property.
var myNewLocalSharedObject:SharedObject = SharedObject.getLocal("myNewLocalSharedObject");

if (myNewLocalSharedObject.size > 0)
{
    trace("Existing Local Shared Object");
}
else
{
    trace("New Local Shared Object");
}
The getLocal() method has three parameters. They should be used as follows:
  • The name parameter is required to assign a name to the Local Shared Object.
  • The localPath parameter is optional and should be used if you anticipate multiple SWF files from the SAME domain needing access to one Local Shared Object, or if the SWF file responsible for creating the Local Shared Object may be moved to another location within the SAME domain.
  • The secure parameter is optional and is used to create a secure Local Shared Object. Once created, any subsequent calls to the secure Local Shared Object must be made by a SWF delivered over HTTPS.

Access / update a Local Shared Object

A Local Shared Object may have many attributes. For example, customer data may include name, age, address and order IDs. Each of these attributes is represented by a property of the Local Shared Object's data property. These values can be accessed, updated or deleted.
// retrieve a reference to the existing Local Shared Object (customerData.sol)
var so:SharedObject = SharedObject.getLocal("customerData");

// accessing the values of the Local Shared Object
var name:String = so.data.name;
var age:int = so.data.age;

// updating the values of the Local Shared Object
so.data.name= "John Smith";
so.data.age = 23;
so.data.orderIds = [23456, 24444, 25432];

// a custom class must be registered and marked serializable using the [RemoteClass] metadata tag
var address:Address = new Address();
address.street = "125 Foo Lane";
so.data.address = address;

// deleting values of the Local Shared Object
delete so.data.age;
delete so.data.orderIds;

Persist a Local Shared Object

Use the flush() method to immediately write a Local Shared Object to the user's computer.
public function flush(minDiskSpace:int = 0):String
A call to flush() returns one of two possible values, SharedObjectFlushStatus.PENDING or SharedObjectFlushStatus.FLUSHED. If more space is needed on the user's computer to persist the Local Shared Object, SharedObjectFlushStatus.PENDING is returned and a dialog box is presented to the user to obtain permission to allocate more space.



When the user selects "Allow" or "Deny" a NetStatusEvent.NET_STATUS event is dispatched targeting the Local Shared Object. Register an event listener to handle this event.
so.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
If the Local Shared Object is successfully written to the user's computer, SharedObjectFlushStatus.FLUSHED is returned and the user is not notified.

The flush() method has one parameter. It should be used as follows:
  • The minDiskSpace parameter is optional and is used to allocate additional space (over 100 kb) on the user's computer for the Local Shared Object. If used, a dialog box is presented to the user asking for permission to allocate additional space.
private function writeSharedObject():void
{
    var so:SharedObject = SharedObject.getLocal("mySO");
    so.data.name = "Jeremy Mitchell";
    try
    {
        // wrap in a try to handle a scenario where local storage has been disallowed
        so.flush(500000);
    }
    catch (e:Error)
    {
        trace("Local storage has been disabled for this domain");
    }
}
Note: The flush() method is not required to persist a Local Shared Object. When the application SWF closes, an attempt is made to persist all Local Shared Objects on the user's computer. However, if adequate space is not allocated for the Local Shared Object, the attempt will fail silently. Use the flush() method to ensure success and recover from any possible failure.

Delete a Local Shared Object

To purge the data of a Local Shared Object and delete it from the user's computer call the clear() method.
public function clear():void
For example:
var so:SharedObject = SharedObject.getLocal("loginInfo");
so.clear();