widged.com
 

Question

Why use partAdded rather than HostComponent to call a function? (from MXML) What are the disadvantage and disadvantage.

Answer

Note. The major project in Flex 4 was one that required no skinning. A research tool for academic clients. Complex functionalities but no design. The default Spark look and feel was just fine. Other projects in Flex 4 were about taking over from other developers, getting the app to work. Skinnable components were mostly simple (pretty buttons), not composite.

When no Skin is defined, then event listeners can be attached to the host component.

Provided the visual component is written using a separation of Display Object from skin, then the button gets declared in a skin, not in the component. Behaviour/EventListeners should never be declared in the skin. (That I would not consider doing.)

“The component class is responsible for controlling the behavior of the subcomponents. The skin class is responsible for defining the subcomponents, including the appearance of the component, its subcomponents, and any other visual aspects of the component.” (source: http://help.adobe.com/en_US/flex/using/WS460ee381960520ad-2811830c121e9107ecb-7ff9.html#WS03d33b8076db57b9-a120b14121ef5f63a3-8000).

If the component is composite, then events cannot be attached when the host component is being initialized. Children have not been created yet. PartAdded and PartRemoved are the best efficient point in the lifecycle to attach these events.

If the component is not composite, like for a skinnable button, then a priori, a click be attached directly to the host component. This is the way it is done in code I had to work in “get this to work” scenarios.

<s:Button skinClass=“path.to.skins.BackButtonSkin” click=“backlickHandler(event)” />

A problem, though, with this type of approach is that a strong reference to the object is created, outside of the object itself, causing the garbage collector to bypass the object.

partAdded, partRemoved offer a cleaner way to manage events.

If the skin is dynamically swapped at runtime (via some “select a theme menu”), then partRemoved will clean up the events from one skin before loading the next one. (The skin has a lifecycle of its own.)

When is partRemoved actually called? What components that have state changes, like an accordion view with non visible panels and subpanels re-created or “destroyed” when creationChildren is false? Not too clear ⇒ Spark Skinnning Specification

Steps

Step 1 : Google for quick orientation

The one from a one minute google search is : The flex coder mailing list tells me that Flex 4 skin can't bind to host component property. Alex Harui himself says that “the whole partAdded protocol is there to: 1) avoid the cost of binding, 2) support changing skins at runtime.”

(Alex Harui excels at pointing at intricacies that escape most developers)

Step 2 : Underlying issues?

  1. Unmanaged EventListeners/Bindings are a frequent source of memory leaks. It is important to understand how any approach to binding can interfere with Garbage Collection.
  2. Changes in Flex 4. Skin decoupled from view. How does it affect the component lifecycle? In what order do things get constructed, what order destructed. What happens when a view is destroyed? Only the view, the view and the skin?
  3. What would be the optimal way to do binding in Flex 4? Same as in Flex 3? Different?

Extra. Why is the method that I used to shortcut binding in Flex 3 not adequate in Flex 4?

Step 3 : Looking into resources and notes on my computer

Overview

Each Spark component consists of two classes: a declarative, MXML-based skin class and an ActionScript component class. The skin class contains everything related to the visual appearance of the Spark component, while the ActionScript class contains everything related to the functional logic of the component.

Every Spark component class defines three very important elements: the data the component expects, the constituent parts that make up the component, and the states the component can enter and exit. In the corresponding skin class, the skin defines how that data is visually displayed, how the parts are laid out and visualized, and what the component looks like as it enters and exits different states. These three key elements— data, parts, and states—define the skinning contract upon which Spark is founded.

(source: Spark Intro @Adobe Articles)

partAdded

Some skinnable components are composed of one or more subcomponents. For example, a NumericStepper component contains a subcomponent for an up button, a down button, and a text area.

The component class is responsible for controlling the behavior of the subcomponents. The skin class is responsible for defining the subcomponents, including the appearance of the component, its subcomponents, and any other visual aspects of the component.

Flex clearly defines the relationship between the component class and the skin class. The component class must do the following:

  • Identify the skin parts that it expects with the [SkinPart] metadata tag. The skin parts are implemented in the skin file. For more information on using the [SkinPart] metadata tag, see SkinPart metadata tag.
  • Identify the view states that the component supports with the [SkinStates] metadata tag. For more information on the [SkinState] metadata tag, see SkinState metadata tag.
  • Use CSS styles to associate the skin class with the component.

The skin class must do the following:

  • Specify the component name with the [HostComponent] metadata tag. While the [HostComponent] metadata tag is not required, it is strongly recommended. For more information on the [HostComponent] metadata tag, see HostComponent metadata tag.
  • Declare the view states, and define their appearance.
  • Define the appearance of the skin parts. The skin parts must use the same name as the corresponding skin-part property in the component.

Flex calls the partAdded() and partRemoved() methods automatically when a skin is created or destroyed. You typically override the partAdded() method to attach event handlers to a skin part, configure a skin part, or perform other actions when a skin part is added. You implement the partRemoved() method to remove the even handlers added in partAdded().

(source: Adobe docs)

Life cycle

Spark built atop of Halo, Skinnable Component extends UIComponent.

  • Skinnable component leaves the same lifecycle in spark than in halo.
  • the Skin is a child to the component and lives its own life cycle
    • createChildren() of SkinnableComponent calls validateSkinState() which in turn calls attachSkin() …
    • attachSkin() creates the skin and adds it as a child …
    • which in turn kicks off the life cycle of the skin
    • attachSkin() also calls findSkinParts() which looks though the children of the skin and populates our declared static part references
    • findSkinParts() calls partAdded() for all the static parts it finds
    • also throws an exception if it does not find a required part
    • at a later time, when you create a dynamic part using createDynamicPartInstance() that method calls partAdded() as well

Flex 4 component lifecycle, presentation by Mrinal Wadhwa

Step 4 : Look at how Adobe engineers do it

Numeric Stepper


[SkinPart(required="true")]
    
    /**
     *  A skin part that defines a TextInput control 
     *  which allows a user to edit the value of
     *  the NumericStepper component. 
     *  ...
     */
    public var textDisplay:TextInput;


    /**
     *  @private
     */
    override protected function setValue(newValue:Number):void
    {
        super.setValue(newValue);
        
        applyDisplayFormatFunction();
    }
    ....
    
    /**
     *  @private
     *  Helper method that applies the valueFormatFunction  
     */
    private function applyDisplayFormatFunction():void
    {
        if (valueFormatFunction != null)
            textDisplay.text = valueFormatFunction(value);
        else
            textDisplay.text = value.toString();
    }

    /**
     *  @private
     */
    override protected function partAdded(partName:String, instance:Object):void
    {
        super.partAdded(partName, instance);
        
        if (instance == textDisplay)
        {
            textDisplay.addEventListener(FlexEvent.ENTER,
                                       textDisplay_enterHandler);
            textDisplay.addEventListener(FocusEvent.FOCUS_OUT, 
                                       textDisplay_focusOutHandler); 
            textDisplay.focusEnabled = false;
            textDisplay.maxChars = _maxChars;
            // Restrict to digits, minus sign, decimal point, and comma
            textDisplay.restrict = "0-9\\-\\.\\,";
            textDisplay.text = value.toString();
            // Set the the textDisplay to be wide enough to display
            // widest possible value. 
            textDisplay.widthInChars = calculateWidestValue(); 
        }
    }
    
    /**
     *  @private
     */
    override protected function partRemoved(partName:String, instance:Object):void
    {
        super.partRemoved(partName, instance);
        
        if (instance == textDisplay)
        {
            textDisplay.removeEventListener(FlexEvent.ENTER, 
                                          textDisplay_enterHandler);
        }
    }

Numeric Stepper Skin

<?xml version="1.0" encoding="utf-8"?>

<!--

    ADOBE SYSTEMS INCORPORATED
    Copyright 2008 Adobe Systems Incorporated
    All Rights Reserved.

    NOTICE: Adobe permits you to use, modify, and distribute this file
    in accordance with the terms of the license agreement accompanying it.

-->

<!--- The default skin class for a Spark NumericStepper component. The skin for the text input field on a NumericStepper
component is defined by the NumericStepperTextInputSkin class.  

       @see spark.components.NumericStepper
       @see spark.skins.spark.NumericStepperTextInputSkin
        
      @langversion 3.0
      @playerversion Flash 10
      @playerversion AIR 1.5
      @productversion Flex 4
-->
<s:SparkSkin xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark"
      xmlns:fb="http://ns.adobe.com/flashbuilder/2009" minHeight="23" minWidth="40" 
      alpha.disabled="0.5">

    <fx:Metadata>
    <![CDATA[ 
        /** 
         * @copy spark.skins.spark.ApplicationSkin#hostComponent
         */
        [HostComponent("spark.components.NumericStepper")]
    ]]>
    </fx:Metadata> 
    
    <fx:Script fb:purpose="styling">
        /* Define the skin elements that should not be colorized. 
           For numeric stepper, the skin itself is colorized but the individual parts are not. */
        static private const exclusions:Array = ["textDisplay", "decrementButton", "incrementButton"];

        /**
         * @private
         */  
        override public function get colorizeExclusions():Array {return exclusions;}
        
        /**
         * @private
         */
        override protected function initializationComplete():void
        {
            useChromeColor = true;
            super.initializationComplete();
        }
        
        private var cornerRadiusChanged:Boolean;
        private var borderStylesChanged:Boolean;
        
        /**
         *  @private
         */
        override protected function commitProperties():void
        {
            super.commitProperties();
            
            if (cornerRadiusChanged)
            {
                var cr:Number = getStyle("cornerRadius");
                if (incrementButton)
                    incrementButton.setStyle("cornerRadius", cr);
                if (decrementButton)
                    decrementButton.setStyle("cornerRadius", cr);
                cornerRadiusChanged = false;
            }
            
            if (borderStylesChanged)
            {
                textDisplay.setStyle("borderAlpha", getStyle("borderAlpha"));
                textDisplay.setStyle("borderColor", getStyle("borderColor"));
                textDisplay.setStyle("borderVisible", getStyle("borderVisible"));
                borderStylesChanged = false;
            }
        }
        
        /**
         *  @private
         */
        override public function styleChanged(styleProp:String):void
        {
            var allStyles:Boolean = !styleProp || styleProp == "styleName";

            super.styleChanged(styleProp);
            
            if (allStyles || styleProp == "cornerRadius")
            {
                cornerRadiusChanged = true;
                invalidateProperties();
            }
            
            if (allStyles || styleProp.indexOf("border") == 0)
            {
                borderStylesChanged = true;
                invalidateProperties();
            }
        }
    </fx:Script>
    
    <s:states>
        <s:State name="normal" />
        <s:State name="disabled" />
    </s:states>

    <!--- The default class is NumericStepperIncrementButtonSkin.
           @copy spark.components.Spinner#incrementButton
           @see spark.skins.spark.NumericStepperIncrementButtonSkin -->
    <s:Button id="incrementButton" right="0" top="0" height="50%" tabEnabled="false" 
              skinClass="spark.skins.spark.NumericStepperIncrementButtonSkin" />
              
    <!--- The default class is NumericStepperDecrementButtonSkin.
            @copy spark.components.Spinner#decrementButton
            @see spark.skins.spark.NumericStepperDecrementButtonSkin -->
    <s:Button id="decrementButton" right="0" bottom="0" height="50%" tabEnabled="false" 
              skinClass="spark.skins.spark.NumericStepperDecrementButtonSkin" />
                           
    <!--- The default class is NumericStepperTextInputSkin.
            @copy spark.components.NumericStepper#textDisplay
            @see spark.skins.spark.NumericStepperTextInputSkin -->
    <s:TextInput id="textDisplay" left="0" top="0" right="18" bottom="0"
        skinClass="spark.skins.spark.NumericStepperTextInputSkin" />
        
</s:SparkSkin>

 
en/computers/lg/actionscript/actionscript-3/spark/partadded.txt · Last modified: 2012/03/22 20:02 by marielle
 
RSS - Banner by widged, template © 7throot HeadQuarters, 2007