Home > Flex > Creating Actionscript Components – FlashBuilder and PureMVC Part IV

Creating Actionscript Components – FlashBuilder and PureMVC Part IV

April 26, 2010

Continuing from part III of this series, this section begins the discussion of the core views.  The ApplicationFacade, ApplicationMediator, and low-level proxies are all essentially the same as the original tutorial.  That tutorial used the stage as the core viewComponent.  All views were constructed from Sprites.  The current Flex makeover uses custom Flex components built in Actionscript, all of which are based off UIComponent.

This is a good opportunity to review Flex component development in Actionscript.  The fundamental life cycle of a component includes initialization, updating, and destruction.  The initialization phase itself consists of four sub-phases.  These include creation (creating the component via the new operator), construction (internal property assignment), attachment (addition to display list), and initialization (full invalidation/validation sweep).

Component updating occurs via user interaction and method calls using an invalidation/validation model, sometimes called 3-phase validation.  The 3-phase model corresponds to individual phases in a marshaled time-slice inside a frame in ASVM-2.  Details are beyond the scope of this post, but I’ll point out some references if there is sufficient interest in a subsequent post.  The 3-phase validation consists of

Update properties ( invalidateProperties() -> commitProperties() )

Update size ( invalidateSize() -> measure() )

Update drawing ( invalidateDisplayList() -> updateDisplayList() )

This whole process of lazy validation essentially offloads tasks that could be done immediately to a later time where processing of that task might be more optimal.  The reason that deference may be more efficient is due to the way time slices (not frames as there are usually multiple time-slices inside a frame) are processed in the flash player.

The process works by using a store -> mark -> invalidate procedure.  Take a text field property, for example.  Instead of updating the text field immediately, the property is stored.  It is marked as changed (by a Boolean variable, for instance).  Properties are invalidated.  If updating the property causes the size of the component to change, then size is invalidated.  The invalidation process informs the Flex framework that one or more phases of the component update cycle are invalid.  The component architecture decides on the best time to perform those updates and calls component methods to perform the updates.

Destruction of the component includes detachment and preparation for garbage collection.  This process is not illustrated in the current tutorial as all components exist for the life of the application.  Usually, this is performed by adding a dispose() or destroy() method to the component.

This is best learned by practicing with very simple components and the current tutorial makeover is a perfect opportunity for such practice.  Let’s begin with the ProgressView.  This view is activated any time something is loaded during the application.  The original tutorial used this view for handling preloading inside the PureMVC application.  That process was replaced in the makeover with a custom preloader.  The ProgressView is now used for preloading image data and images themselves.  The display is very simple; just a text field with the text “Loading x%”.  Perfect for your very first Flex Actionscript component.

A custom Actionscript component extends UIComponent or another existing component.  Four UIComponent methods are overridden to implement the initialization and updating parts of the component life cycle.  These are createChildren, commitProperties, measure, and updateDisplayList.  The createChildren method is called as part of the initialization phase of the component, whenever it is added to the display list.  Since a component could be reparented, some care should be taken to only create children if they do not already exist.  This is the place to create component children that persist through the entire life of the component.  The commitProperties method is called during the first full validation pass during initialization and whenever invalidateProperties is called thereafter.  This method handles any property updates and creates/manages children that persist only temporarily in the component life cycle.  The measure method is called during the first validation pass and whenever invalidateSize is called thereafter.  It ensures the component is properly measured or sized.  Subsequent accesses to the component’s width and height should return the correct size of the component.  This method is not called, however, if width and height are explicitly set.  UpdateDisplayList does just that.  It ensures all children are properly positioned and ready to render :)

This is a very simplistic overview, just sufficient to make this tutorial self-contained.  A wealth of information is available in the Flex help and online if you wish to purse these topics in more detail. The question at hand is how to implement this component model for the ProgressView.  We could easily use a label, but it is helpful to show how low-level items such as text fields can be implemented in a component, so let’s stick with the text field used in the original tutorial.

In addition to the symbolic constants provided in the original tutorial, the following variables are used to implement the store -> mark -> invalidate procedure,

protected var_textField:TextField;

protected var _text:String;

protected var _textChanged:Boolean;

protected var _isShowing:Boolean;

The actual text is stored in the _text variable.  The _textChanged variable marks whether or not the text has been changed.  Instead of directly setting visibility, the _isShowing variable marks whether or not the component should the showing itself.

The text field is not created on construction.  Its creation is delayed until Flex calls the createChildren method, which happens whenever ProgressView is added to the display list.  As an aside, I’m always wondering if a font is truly embedded.  A TextFormat object is created as part of controlling text display.  Given that object, we can test for embedding with the statements

import mx.core.FlexGlobals;

FlexGlobals.topLevelApplication.systemManager.isFontFaceEmbedded(textFormat)

where textFormat is the reference to the TextFormat object.

The commitProperties method updates the text field’s text if it has changed.  The measure method sets the appropriate measure properties to the text field’s width and height.  The updateDisplayList method centers the text field inside the parent container and adjusts visibility based on the current value of the _isShowing variable.

The show(), hide(), and update() methods from the original tutorial remove the tweening animation, although that could be added as an exercise.  The appropriate properties are updated, then the invalidation methods are called to cause Flex to properly update the component via later calls to corresponding validation methods.  This completes the updating through the invalidate/validate model.

The source for ProgressView is provided below.

package view
{
  import flash.text.TextField;
  import flash.text.TextFieldAutoSize;
  import flash.text.AntiAliasType;
  import flash.text.TextFormat;
  import mx.core.FlexGlobals;
  import mx.core.UIComponent;
  public class ProgressView extends UIComponent
  {
    public static const NAME:String   = 'ProgressView';
    public static const SHOW:String = NAME+'Show';
    public static const HIDE:String = NAME + 'Hide'
    public static const UPDATE:String=NAME + 'Update';
    protected var _textField:TextField;
    protected var _text:String;
    protected var _textChanged:Boolean;
    protected var _isShowing:Boolean;
    public function ProgressView()
    {
      super();    // yeah, it's unnecessary, but old habits die hard :)
      _textChanged = false;
      _isShowing   = false;
      _text        = "";
    }
    override protected function createChildren():void
    {
      super.createChildren();
      if( !_textField )
      {
        var textFormat:TextFormat = new TextFormat();
        textFormat.color         = 0x000000;
        textFormat.font         = "arialEmbedded";
        textFormat.size            = 20;
        _textField                 = new TextField();
        _textField.embedFonts      = true;
        _textField.autoSize        = TextFieldAutoSize.LEFT;
        _textField.antiAliasType   = AntiAliasType.ADVANCED;
        _textField.defaultTextFormat=textFormat;
        _text                   = 'Please wait...';
        _textChanged   = true;
        addChild(_textField);
      }
    }
    override protected function commitProperties():void
    {
      super.commitProperties();
      if( _textChanged )
      {
        _textField.text = _text;
        _textChanged    = false;
      }
    }
    override protected function measure():void
    {
      super.measure();
      measuredWidth  = measuredMinWidth  = _textField.width;
      measuredHeight = measuredMinHeight = _textField.height;
    }
    override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
    {
      super.updateDisplayList(unscaledWidth, unscaledHeight);
      _textField.x = Math.round( 0.5*(parent.width  - _textField.width ) );
      _textField.y = Math.round( 0.5*(parent.height - _textField.height) );
      visible = _isShowing;  // could apply animation or whatever here
    }
    public function show():void
    {
      _text ='Please wait...';
      _textChanged = true;
      _isShowing   = true;
      invalidateProperties();
      invalidateSize();
      invalidateDisplayList();
    }
    public function hide():void
    {
      _isShowing = false;
      invalidateProperties();
      invalidateSize();
      invalidateDisplayList();
    }
    public function update(percent:Number):void
    {
      _text        = 'Loaded'+percent+'%';
      _textChanged = true;
      invalidateProperties();
      invalidateSize();
      invalidateDisplayList();
    }
  }
}

This seems like several levels of overkill just for a text field and in a way, that’s true. The purpose of this section of the tutorial is to introduce Flex component development in AS using very simple components. The benefit of using the 3-phase validation model is that it offloads processing of each phase to the most convenient or efficient time as determined by Flex . Every developer that is familiar with the invalidation/validation model can work on another developer’s component without having to learn a new layout or updating model.

One final note is that while addChild() is used to add a text field to a UIComponent, components are added to the display list of other spark components using addElement() in F4. This is illustrated in the ProgressViewMediator,

override public function onRegister():void

{

progressView = new ProgressView();

viewComponent.addElement(progressView);

sendNotification(URLView.GET_DATA);

}

Note that addElement() causes FlashBuilder to call the ProgressView createChildren() method and kicks off the initialization phase of the ProgressView life cycle.

The next section of the tutorial will discuss the URLView.

About these ads
Categories: Flex Tags: , ,
Follow

Get every new post delivered to your Inbox.

Join 1,333 other followers

%d bloggers like this: