Flex 4 Components
Upcoming SlideShare
Loading in...5
×
 

Flex 4 Components

on

  • 2,609 views

 

Statistics

Views

Total Views
2,609
Views on SlideShare
2,609
Embed Views
0

Actions

Likes
2
Downloads
29
Comments
0

0 Embeds 0

No embeds

Accessibility

Categories

Upload Details

Uploaded via as Microsoft PowerPoint

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

Flex 4 Components Flex 4 Components Presentation Transcript

  • Page 0 of 59
    Flex 4 Components from the Fire Hose
    Michael Labriola
    Senior Consultants
    Digital Primates
    twitter.com/mlabriola
  • Who am I?
    Michael Labriola
    Senior Consultant
    Digital Primates
    Client side architect specializing in Adobe Flex
    Architect and developer of Fluint
    Lead architect and developer of FlexUnit 4.x
    Team Mentor
    Co-Author of Flex Training from the Source Series
    Geek
    Page 2 of 59
  • Pregame
    Pre-compile, compile and linking time
    3
  • What are we going to cover?
    We are going to start with MXML
    We are going to see what it looks like after it is compiled
    We are then going to walk through each class on the way from instantiation through display
    We will cover as much as we can before time runs out
    Page 3 of 59
  • Here is our source code
    Firehose.mxml is our main application file. It consists of the following pieces:
    <?xml version="1.0" encoding="utf-8"?>
    <s:Application
    xmlns:fx="http://ns.adobe.com/mxml/2009"
    xmlns:s="library://ns.adobe.com/flex/spark">
    <s:Button id="btn" label="Click Me"/>
    </s:Application>
    Page 3 of 59
  • Generation
    This code turns into many generated files. Most important to us are:
    ActionScript version of your Application subclass
    ActionScript subclass of the system manager
    Getter/Setter generation for mxml properties
    Flex Init Mixin
    Styles, styles and more styles
    Page 3 of 59
  • Application Subclass
    The first piece of generated code is the Application subclass. When the application is subclassed, several important things occur:
    Frame metadata is specified
    Properties are created for MXML components in the document
    Factory functions are created for the MXML content
    The mxmlContentFactory is set
    Style declaration setup is deferred for a bit
    Page 3 of 59
  • Frame metadata
    Page 3 of 59
    The following metadata is added to the application subclass:
    [Frame(extraClass="_Firehose_FlexInit")]
    [Frame(factoryClass="_Firehose_mx_managers_SystemManager")]
    The first line ensures the inclusion of the Firehose_FlexInitmixin
    The second line specifies that the Firehose_mx_managers_SystemManager is the bootstrapped root class for your swf
  • MXML Properties
    Page 3 of 59
    A Bindable public property is created for the MXML instances so that you can refer to these by id in the Application
    [Bindable]
    public varbtn : spark.components.Button;
    Note this corresponds to the id of the object in the MXML
    <s:Button id="btn" label="Click Me"/>
  • Factory Functions
    Page 3 of 59
    For each MXML tag, a factory function is created to build the corresponding object and set its properties
    private function _Firehose_Button1_i():Button {
    var temp : Button = new spark.components.Button();
    temp.label = "Click Me";
    temp.id = "btn";
    if (!temp.document) temp.document = this;
    btn = temp;
    BindingManager.executeBindings(this, "btn", btn);
    return temp;
    }
  • Factory Functions
    Page 3 of 59
    An Array of all of the components is then created invoking each of those methods and adding the result to the Array
    private function _Firehose_Array1_c() : Array
    {
    var temp : Array = [_Firehose_Button1_i()];
    return temp;
    }
  • Multiple Controls
    Page 3 of 59
    For example, if we had three buttons as peers, the code would like this:
    <s:Button id="btn1" label="Click Me"/>
    <s:Button id="btn2" label="Click You"/>
    <s:Button id="btn3" label="Click It"/>
    private function _Firehose_Array1_c() : Array {
    var temp : Array = [_Firehose_Button1_i(), _Firehose_Button2_i(),
    _Firehose_Button3_i()];
    return temp;
    }
  • Non Skinnable
    Page 3 of 59
    Flex 4 is full of new classes to learn. Let’s start by discussing an old favorite, UIComponent and a new addition Group.
    UIComponent is still the base class of all components and containers in Flex 4.
    Group is the base container class. It can hold an unknown number of elements which may be defined in MXML and is akin to a lighter-weight version of Container from the mx component set
  • Skinnable*
    Page 3 of 59
    Two additional new classes in Flex 4 worth noting at this time: SkinnableComponent and SkinnableContainer
    The SkinnableComponent class is the base class for all components where the view has been separated from component logic through the use of a skin. It is a UIComponent subclass
    The SkinnableContainer class is a subclass of SkinnableComponent which allows for both a skin and unknown additional elements which may be defined in MXML
  • mxmlContentFactory
    Page 3 of 59
    The goal of all of the work in the generated application code so far has been to set the mxmlContentFactory property of the Firehose class (Application subclass).
    This property is defined in SkinnableContainer and allows us to specify those unknown MXML children. This property is typed as an IDeferredInstance.
    this.mxmlContentFactory =
    new DeferredInstanceFromFunction(_Firehose_Array1_c);
  • IDeferredInstance
    Page 3 of 59
    To be an IDeferredInstance a class must have a single method:
    public function getInstance():Object {
    ...
    return something;
    }
    An IDeferredInstance defers the creation of an object until its getInstance() method is called. Each subsequent call returns the originally created instance
  • DeferredInstanceFromFunction
    Page 3 of 59
    Our generated code is simply a function and the mxmlContentFactory expects the IDeferredInstance. The DeferredInstanceFromFunction handles this issue
    this.mxmlContentFactory =
    new DeferredInstanceFromFunction(_Firehose_Array1_c);
    The DeferredInstanceFromFunction class takes a function as its first parameter. When its getInstance() method is called it invokes that method to create the component graph
  • Nested Controls
    Page 3 of 59
    The factory functions generated change a bit when we have nested controls:
    <s:Button id="btn1" label="Click Me"/>
    <s:Group>
    <s:Button id="btn2" label="Click You"/>
    <s:Button id="btn3" label="Click It"/>
    </s:Group>
  • Nested Controls Generated
    Page 3 of 59
    private function _Firehose_Array1_c() : Array {
    var temp : Array = [_Firehose_Button1_i(), _Firehose_Group1_c()];
    return temp;
    }
    private function _Firehose_Button1_i() : Button { ... }
    private function _Firehose_Group1_c() : Group {
    var temp : Group = Group();
    temp.mxmlContent = [_Firehose_Button2_i(), _Firehose_Button3_i()];
    if (!temp.document) temp.document = this;
    return temp;
    }
  • Nested Group
    Page 3 of 59
    In the case of the nested group, you may notice that the mxmlContent property is set, instead of the mxmlContentFactory.
    var temp : Group = Group();
    temp.mxmlContent = [_Firehose_Button2_i(), _Firehose_Button3_i()];
    Further, the property is set directly to the Array built by calling these methods. The mxmlContent property is the final home of all children. In this case they are created immediately instead of deferred
  • Application Subclass
    The next major piece of generated code is the SystemManager subclass. Several important things occur here:
    The subclass implements IFlexModuleFactory
    The subclass implements ISWFContext
    The create() method is overridden
    The info() method is overridden
    Page 3 of 59
  • IFlexModuleFactory
    Page 3 of 59
    The newly created SystemManager subclass will implement IFlexModuleFactory and ISWFContext which requires a number of interesting methods. The most interesting ones to us at this moment are:
    function registerImplementation(interfaceName:String, impl:Object):void;
    function getImplementation(interfaceName:String):Object;
    function create(... parameters):Object;
    function info():Object;
  • registerImplementation()
    Page 3 of 59
    This method allows the Flex framework, and you should you wish, to register Singletons that implement a specific interface. It is sort of a Dependency Injection registration meets singleton and had a baby scenario:
    public function registerImplementation(interfaceName:String,
    impl:Object):void;
    You call this method with an interface you wish to register and an implementing object.
  • getImplementation()
    Page 3 of 59
    This method allows you to getImplementations that were previously registered. So, for example, if you wanted to register a given singleton you could then later retrieve it through the SystemManager
    public function getImplementation(interfaceName:String):Object
  • create()
    Page 3 of 59
    The create() method is where the magic happens and your app will be instantiated:
    override public function create(... params):Object {
    ...
    varmainClassName:String =
    params.length == 0?"Firehose“:String(params[0]);
    varmainClass:Class = Class(getDefinitionByName(mainClassName));
    if (!mainClass) return null;
    varinstance:Object = new mainClass();
    if (instance is IFlexModule)
    (IFlexModule(instance)).moduleFactory = this;
    return instance;
    }
  • info()
    Page 3 of 59
    The info() method returns a generic object filled with required and optional properties used to configure the system.
    Examples of data you may find in the object returned by info():
    RSL data, compiled locales, resource bundle names, application domain, main class name, mixins and the correct preloader to use
  • info()
    Page 3 of 59
    In this code snippet from info() you can see where the mixins are specified. This is the way in which Flex applies some of the additional generated code to the system manager.
    _info = {

    mainClassName: "Firehose",
    mixins: [ "_Firehose_FlexInit", "_Firehose_Styles" ],
    preloader: mx.preloaders.SparkDownloadProgressBar
    }
  • Mixin
    Page 3 of 59
    Mixin classes are decorated with the Mixin metadata and have a public static method named init() that takes a IFlexModuleFactory (SystemManager in this case) as an argument.
    The mixin effectively sets properties and instances on the SystemManager to create the StyleManager, register styles and more
  • Btn Binding Setup
    Page 3 of 59
    Earlier I mentioned that Flex creates a public var for btn on the host component because we have an MXML control named btn. That was true then.
    However, this is Flex, and in Flex all MXML components are bindable, therefore, that public var actually becomes a getter and setter before we are done
  • Btn Binding Setup
    Page 3 of 59
    [Bindable(event="propertyChange")]
    public function get btn():spark.components.Button {
    return this._97884btn;
    }
    public function set btn(value:spark.components.Button):void{
    varoldValue:Object = this._97884btn;
    if (oldValue !== value) {
    this._97884btn = value;
    if (this.hasEventListener("propertyChange"))
    this.dispatchEvent( PropertyChangeEvent.createUpdateEvent(
    this, "btn", oldValue, value));
    }
    }
  • Watcher Setup
    Page 3 of 59
    If you add one Bindable properties to your application. You receive the following additons, which is the code watching for Bindable changes.
    <?xml version="1.0" encoding="utf-8"?>
    <s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
    xmlns:s="library://ns.adobe.com/flex/spark">
    <fx:Script> <![CDATA[
    [Bindable]
    public varlblMe:String;
    ]]>
    </fx:Script>
    <s:Button id="btn" label="{lblMe}"/>
    </s:Application>
  • lblMe Binding Setup
    Page 3 of 59
    private function _Firehose_bindingsSetup():Array {
    varresult:Array = [];
    result[0] = new mx.binding.Binding(this,
    null,
    null,
    "btn.label"
    , "lblMe");
    return result;
    }
  • lblMe Watcher Setup
    Page 3 of 59
    public function setup(target:Object,
    propertyGetter:Function,
    staticPropertyGetter:Function,
    bindings:Array,
    watchers:Array):void {
    watchers[0] = new mx.binding.PropertyWatcher("lblMe",
    {propertyChange: true },
    [bindings[0]],
    propertyGetter );
    watchers[0].updateParent(target);
    }
    }
    }
  • Application Constructor
    Page 3 of 59
    if (_watcherSetupUtil == null) {
    varwatcherSetupUtilClass:Object = getDefinitionByName("_FirehoseWatcherSetupUtil");
    watcherSetupUtilClass["init"](null);
    }
    _watcherSetupUtil.setup(this,
    function(propertyName:String):*
    { return target[propertyName]; },
    function(propertyName:String):*
    { return Firehose[propertyName]; },
    bindings, watchers);
    mx_internal::_bindings = mx_internal::_bindings.concat(bindings);
    mx_internal::_watchers = mx_internal::_watchers.concat(watchers);
    for (var i:uint = 0; i < bindings.length; i++) {
    Binding(bindings[i]).execute();
    }
  • GAME TIME
    Loading and Running
    35
  • SWF Load
    Page 3 of 59
    You begin loading your SWF into the browser of your choice. However, you need to remember that SWF is a streaming format, so it arrives a frame at a time.
    Thanks to your frame metadata, you already told the SWF it should instantiate your SystemManager… so it does.
  • SystemManager Constructor
    Page 3 of 59
    The SystemManager’s constructor is called where it does a few things like figure out if it is the top level or if it has been loaded into another SWF.
    If it is the top, it sets alignments and scale modes on the stage and then does something extremely important:
    stop();
    We need to wait until we have loaded everything we need before we advance to the next frame and start the application
  • SystemManager Constructor
    Page 3 of 59
    The final line in the SystemManager’s Constructor:
    if (root && root.loaderInfo)
    root.loaderInfo.addEventListener(Event.INIT, initHandler);
    It waits until an INIT event is broadcast from the loaderInfo object. This event is broadcast the moment once everything needed by this first frame is available and the SystemManager’sConnstructor finishes
  • init()
    Page 3 of 59
    One the initHandler() is called, the world gets more interesting.
    This method does a little cleanup, removes the existing init event listener and some other pieces, however, the two critical things it does for this presentation:
    Adds another event listener for a method named docFrameListener which is called when the rest of the application is loaded
    Calls the initialize() method
  • initialize()
    Page 3 of 59
    This method is responsible for creating the PreLoader. The PreLoader itself is a logical class, but it allows us to specify a display class which is displayed to the user to make a better user experience while we load the remainder of the app.
    This method also starts downloading any RSLs needed for your application… and some it doesn’t need
    Then we wait until the all of the RSLs are loaded and the remainder of the app is loaded
  • kickOff()
    Page 3 of 59
    When both of these conditions are true (and they can happen in any order) either the preloader or the docFrameHandler catches this fact and ensures kickOff() is called.
    This method registers a ton of classes as singletons to support everything from fonts to drag and drop.
    It also instantiates all of those mixins we discussed in the pregame, adding those pieces to the system manager at this time
  • initializeTopLevelWindow()
    Page 3 of 59
    This method does a lot of work to ensure we load correctly and size appropriately, however, the part that interests us most:
    childManager.initializeTopLevelWindow(w, h);
    ChildManager is an instance of a class that implements ISystemManagerChildManager, meaning that it has methods to handle the result of adding and removing children as well as the initialize method we call here.
    In this case, it is an instance of ChildManager
  • ChildManager()
    Page 3 of 59
    Inside of ChildManager, the create() method that was overridden by the compiler is called, effectively creating the instance of your application.
    A creationComplete event handler is added to the application and the preloader is informed of the application’s existence so that it may leave gracefully when the application is ready
    Note, the application is created but not yet added to the System Manager… that will happen much later
  • ChildManager() cont
    Page 3 of 59
    Inside of the ChildManager, the addingChild() method is called.
    This method sets the nestLevel for the new component (nestLevel increase as depth of the component increases)
    The childAdded() method is called next, which dispatches an ADD event for the child and calls the child’s initialize…
    And the fun begins
  • PreInitialize
    Page 3 of 59
    The Application initializes several managers and the context menus before beginning the component initialization cycle.
    This starts by broadcasting FlexEvent.PREINITIALIZE. This is one of the most important events in all of Flex.
    It means the display object dispatching is initialized, however, it has yet to create any of its visual children. Anything that you want to do which will affect the number or type of children should be done NOW
  • createChildren()
    Page 3 of 59
    Immediately after dispatching the event, the createChildren() method is called. The createChildren() method is responsible for creating all relatively static visual children of a component.
    In non-skinnable components this method created all visual children directly. In skinnable components, this method invokes validateSkinChange() (which is invoked now and when the skin changes at runtime)
  • validateSkinChange()
    Page 3 of 59
    The validateSkinChange() checks for an existing skin and detaches it if it exists. It then performs the most important operation here, it calls attachSkin()
    ..
    if (skin)
    detachSkin();
    attachSkin();
    ..
  • attachSkin()
    Page 3 of 59
    The attachSkin() method finds the correct skin for this component, instantiates it, and passes the result to the setSkin() method.
    In our application, this means creating an instance of the ApplicationSkin. ApplicationSkin is a Skin, which is just a Group and ultimately a UIComponent, so it will have this same life cycle recursively.
    The setSkin() method sets the _skin property of the class and dispatches a skinChanged event
  • attachSkin()
    Page 3 of 59
    At this point, the owner and hostComponent of the skin are set. The hostComponent of a skin always refers to the object for which it is providing a visual display.
    Note, not the hostComponent and owner property is set after the class skin instantiated. You cannot access either of these properties during the skin’s construction
  • attachSkin()
    Page 3 of 59
    After the skin is created, the styles of the component are passed to the skin for use.
    At this point, the skin is added to the component, kicking off its life cycle. The addingChild() method is called, followed by actually adding it via the player APIs. Then the childAdded() method is called.
    The childAdded method, much like it did for the application, causes the ApplicationSkin’s initialize method to be called
  • Skin PreInitialize
    Page 3 of 59
    The skin now broadcasts its FlexEvent.PREINITIALIZE.
    It means the skin is initialized, however, it has yet to create any of its visual children.
    I reiterate, Anything that you want to do which will affect the number or type of children should be done NOW
  • Skin createChildren()
    Page 3 of 59
    Immediately after dispatching the event, the createChildren() method is called.
    Within the skin, the setMXMLContent method is called. This method takes an array of MXML objects defined in the skin (much like the array you saw in the nested example).
    All old MXML elements are removed from the skin, and add all of the new elements, using a method named addElement()
  • addElement()
    Page 3 of 59
    Immediately after dispatching the event, the createChildren() method is called.
    Within the skin, the setMXMLContent method is called. This method takes an array of MXML objects defined in the skin (much like the array you saw in the nested example).
    All old MXML elements are removed from the skin, and add all of the new elements. We use the word element here as the items we are adding or removing are of type IVisualElement
  • IVisualElement
    Page 3 of 59
    IVisualElement is a new interface to spark which defines the properties that must be present on an object to be correctly sized, positioned and displayed in any type of spark container
    In spark, controls, containers and even graphic primitives can be IVisualElements. Using this interface is a key component to allowing spark to intermingle these classes
  • elementAdded() cont
    Page 3 of 59
    As the new visual elements are added to the skin, the skin’s elementAdded() method is called.
    In this method, the elementAdded method of the layout object is also called to inform it of a change, and the invalidateLayering() method is called to inform the skin that the layering of objects may have changed.
    This invalidation eventually leads to a call to assignDisplayObjects() which reorders the objects
  • elementAdded()
    Page 3 of 59
    In the case where the element is an IGraphicalElement (an interface that descends from IVisualElement to specifically handle the needs of graphics) a special method called addingGraphicalElement() is called and passed the element.
    IGraphicalElements are not displayObjects on their own like components. They are simply logic and state which draw onto a display object. This means that Flex must identify the correct display object for drawing.
  • elementAdded()
    Page 3 of 59
    In all other cases, the element is assumed to be a displayObject and added to the display list via a method named addObjectToDisplayList()
    This method ensures the child reaches the display list at the appropriate location
    Any listeners are notified of the fact that a new element was added and the invalidateDisplayList() and invalidateSize() methods are called to ensure the skin is sized and repositioned
  • More Recursion
    Page 3 of 59
    The addition of each of these elements to the display list causes their initialize() methods to be called.
    They go through the same process of either instantiating components or instantiating skins, which then instantiate components, which may have skins, with components, with skins….
  • Back Up
    Page 3 of 59
    Starting from the deepest point on the stack of children, each child does the following operations:
    invalidateProperties()
    invalidateSize()
    invalidateDisplayList()
    sets processedDescriptors = true
    dispatches its FlexEvent.INITIALIZE event
  • Child Additions
    Page 3 of 59
    Starting from the deepest point on the stack of children, each children performs the following operations:
    invalidateProperties()
    invalidateSize()
    invalidateDisplayList()
    sets processedDescriptors = true
    dispatches its FlexEvent.INITIALIZE event
  • FlexEvent.INITIALIZE
    Page 3 of 59
    The initialize event is always broadcast by the inner most child first.
    It signifies that all of the visual children have been created, however, the children nor the component have been sized or positioned at this time.
    This is a great place to make visual modifications to children.
  • findSkinParts()
    Page 3 of 59
    After each skin initializes, you return to the attachSkin() method where the skin was first added.
    We now execute a method named findSkinParts(). This method looks through the SkinParts defined via the [SkinPart] metadata in any SkinnableComponent subclass.
    If it finds an element with a matching id in the skin, it provided the variable annotated with the [SkinPart] metadata with a reference to that part in the skin
  • partAdded()
    Page 3 of 59
    As each of these parts is identified and the reference provided, the partAdded() method of SkinnableComponent is called and provided both the string name of the skin part and a reference to it:
    if (this[id] != null && !(this[id] is IFactory))
    partAdded(id, this[id]);
    It is common to override this method in your own components to configure each skin part as it is added. There is a parallel method called partRemoved() which is called with the same arguments if a part is removed.
  • partAdded()
    Page 3 of 59
    The default behavior of the partAdded() method is to dispatch a SkinPartEvent.PART_ADDED event with the partName and instance which can also be used for configuration or other logic
  • SkinStates
    Page 3 of 59
    After all parts are added, the invalidateSkinState() method is called.
    When you define a SkinnableComponent, you can specify required SkinStates that the skin must fulfill. Meaning it will have those states defined in the skin.
    [SkinState("up")]
    [SkinState("over")]
    --- --- --- --- --- ---
    <s:states>
    <s:State name="up" />
    <s:State name="over" />
    </s:states>
  • SkinStates
    Page 3 of 59
    The invalidateSkinState() method informs the component that the skin needs to know its new visual state
    As a result of this call, the getCurrentSkinState() method of this component will be called asynchronously. It is expected to return a string that matches one of those valid states
    It can derive this information in any way it chooses and is not bound to the component state
  • Deferred
    Page 3 of 59
    After all of the skins are attached, the stack returns to the createChildren() method
    Here it calls a method named createDeferredContentIfNeeded()
    This method looks for any items specified in the mxmlContentFactory that have not yet been instantiated and creates them now… starting yet another recursive cycle. Note, this is where the Flex compiler assigned our original content
  • Deferred Creation
    Page 3 of 59
    This means initialize is a bit trickier.
    Skins for a class are created before MXML content, so for instance the ApplicationSkin will be created well before the Button creation begins
    This is consistent with the use of initialize but, depending on the complexity of the skin may seem strange at first glance
  • Recursion Complete
    Page 3 of 59
    One all of the children are initialized from the inner most to the outer, you will be able to finish the initialize() method of the Application that we started many slides ago.
    Now we need to worry about invalidation.
  • Invalidation
    Page 3 of 59
    All of this invalidation effectively adds each of these components to a priority queue. This priority queue sorts based on the nestLevel that we mentioned earlier.
    As these queues are resolved the outer most item toward the deepest have their validateProperties() method called.
    Then the deepest moving toward the outermost have their validateSize() method called.
    Finally, from the outer toward deepest, the validateDisplayList() method is called
  • Invalidation
    Page 3 of 59
    After each component has had a chance to complete all necessary validation, it is marked as initialized. Not this is different than the INITIALIZE event
    Marking a component as Initialized does two important things.
    it sets the component to visible (if applicable)
    It dispatches a FlexEvent.CREATION_COMPLETE
  • creationComplete
    Page 3 of 59
    The FlexEvent.CREATION_COMPLETE means that all layout, sizing and positioning is complete and the item can now be visible on the screen.
    It is just about the worst time to do anything, save for operations that need to know the size and position of a child
  • Application Visible
    Page 3 of 59
    Once all of the children have broadcast their creationComplete event, the time for the application to complete is here
    The application broadcasts its creationComplete, which is a trigger to System Manager.
    The System Manager destroys the preloader and adds the Application as a child to the System Manager, making it visible.
  • Application Complete
    Page 3 of 59
    The act of making the application a child of the system manager is among the last acts performed before the System Manager broadcasts a FlexEvent.APPLICATION_COMPLETE from the Application.
    This signals that the application is ready to be used.
  • Questions
    Page 3 of 59
    ?
  • Page 59 of 59
    Me
    Michael Labriola
    http://twitter.com/mlabriola