Apalabrados
Apalabrados

Reputation: 1146

Flex Skin addEventListener

I have Spark TextInput with a Skin. This Skin has 3 states and need to be able to change of state depending on the Event listened.

The holder of the TextInput is a Panel which dispatch an Event when a user click on a certain place.

I do not know how to listen such event.

The textInput within the Panel:

<s:TextInput id="nombre" x="10" y="23" width="264" borderVisible="false"
                 chromeColor="#791111" color="#741111" contentBackgroundColor="#ECECEC"
                 focusColor="#6E1212" fontWeight="bold" 
                 skinClass="skins.NormalInputText"
                 text="@{clientesModel.datosCliente.nombre}"/>

and the Skin NormalInputText:

<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" 
alpha.disabledStates="0.5" blendMode="normal" currentState="normal"
creationComplete="init()">

<fx:Metadata>
<![CDATA[ 
    /** 
     * @copy spark.skins.spark.ApplicationSkin#hostComponent
     */
    [HostComponent("spark.components.TextInput")]
]]>
</fx:Metadata> 

<fx:Script fb:purpose="styling">
    <![CDATA[
        import mx.core.FlexVersion;


    private var paddingChanged:Boolean;


    /* Define the skin elements that should not be colorized. */
    static private const exclusions:Array = ["background", "textDisplay", "promptDisplay", "border"];

    /* exclusions before Flex 4.5 for backwards-compatibility purposes */
    static private const exclusions_4_0:Array = ["background", "textDisplay", "promptDisplay"];

    public function init():void {
        parent.addEventListener('panel_mode_edit',  editMode);
        parent.addEventListener('panel_mode_query', queryMode);
        parent.addEventListener('panel_mode_new',   newMode);
    }
    private function editMode(evt:Event):void {
        this.currentState = 'editing';
    }
    private function queryMode(evt:Event):void {
        this.currentState = 'normal';
    }
    private function newMode(evt:Event):void {
        this.currentState = 'adding';
    }

    override public function get colorizeExclusions():Array {
        // Since border is styleable via borderColor, no need to allow chromeColor to affect
        // the border.  This is wrapped in a compatibility flag since this change was added  
        // in Flex 4.5
        if (FlexVersion.compatibilityVersion < FlexVersion.VERSION_4_5)
        {
            return exclusions_4_0;
        }

        return exclusions;
    }

    /* Define the content fill items that should be colored by the "contentBackgroundColor" style. */
    static private const contentFill:Array = ["bgFill"];

    /**
     *  @private
     */
    override public function get contentItems():Array {return contentFill};

    /**
     *  @private
     */
    override protected function commitProperties():void
    {
        super.commitProperties();

        if (paddingChanged)
        {
            updatePadding();
            paddingChanged = false;
        }
    }

    /**
     * @private
     */
    override protected function initializationComplete():void
    {
        useChromeColor = true;
        super.initializationComplete();
    }

    /**
     *  @private
     */
    override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
    {
        if (getStyle("borderVisible") == true)
        {
            border.visible = true;
            shadow.visible = true;
            background.left = background.top = background.right = background.bottom = 1;
            textDisplay.left = textDisplay.top = textDisplay.right = textDisplay.bottom = 1;
            if (promptDisplay)
            {
                promptDisplay.setLayoutBoundsSize(unscaledWidth - 2, unscaledHeight - 2);
                promptDisplay.setLayoutBoundsPosition(1, 1);
            }
        }
        else
        {
            border.visible = false;
            shadow.visible = false;
            background.left = background.top = background.right = background.bottom = 0;
            textDisplay.left = textDisplay.top = textDisplay.right = textDisplay.bottom = 0;
            if (promptDisplay)
            {
                promptDisplay.setLayoutBoundsSize(unscaledWidth, unscaledHeight);
                promptDisplay.setLayoutBoundsPosition(0, 0);
            }
        }

        borderStroke.color = getStyle("borderColor");
        borderStroke.alpha = getStyle("borderAlpha");

        super.updateDisplayList(unscaledWidth, unscaledHeight);
    }

    /**
     *  @private
     */
    private function updatePadding():void
    {
        if (!textDisplay)
            return;

        // Push padding styles into the textDisplay
        var padding:Number;

        padding = getStyle("paddingLeft");
        if (textDisplay.getStyle("paddingLeft") != padding)
            textDisplay.setStyle("paddingLeft", padding);

        padding = getStyle("paddingTop");
        if (textDisplay.getStyle("paddingTop") != padding)
            textDisplay.setStyle("paddingTop", padding);

        padding = getStyle("paddingRight");
        if (textDisplay.getStyle("paddingRight") != padding)
            textDisplay.setStyle("paddingRight", padding);

        padding = getStyle("paddingBottom");
        if (textDisplay.getStyle("paddingBottom") != padding)
            textDisplay.setStyle("paddingBottom", padding);

        if (!promptDisplay)
            return;

        padding = getStyle("paddingLeft");
        if (promptDisplay.getStyle("paddingLeft") != padding)
            promptDisplay.setStyle("paddingLeft", padding);

        padding = getStyle("paddingTop");
        if (promptDisplay.getStyle("paddingTop") != padding)
            promptDisplay.setStyle("paddingTop", padding);

        padding = getStyle("paddingRight");
        if (promptDisplay.getStyle("paddingRight") != padding)
            promptDisplay.setStyle("paddingRight", padding);

        padding = getStyle("paddingBottom");
        if (promptDisplay.getStyle("paddingBottom") != padding)
            promptDisplay.setStyle("paddingBottom", padding);
    }

    /**
     *  @private
     */
    override public function styleChanged(styleProp:String):void
    {
        var allStyles:Boolean = !styleProp || styleProp == "styleName";

        super.styleChanged(styleProp);

        if (allStyles || styleProp.indexOf("padding") == 0)
        {
            paddingChanged = true;
            invalidateProperties();
        }
    }
    ]]>
</fx:Script>

<fx:Script>
    <![CDATA[
    /** 
     * @private 
     */     
    private static const focusExclusions:Array = ["textDisplay"];

    /**
     *  @private
     */
    override public function get focusSkinExclusions():Array { return focusExclusions;};
    ]]>
</fx:Script>

<s:states>
    <s:State name="normal"/>
    <s:State name="editing" />
    <s:State name="adding" />
    <s:State name="disabled" stateGroups="disabledStates"/>
    <s:State name="normalWithPrompt"/>
    <s:State name="disabledWithPrompt" stateGroups="disabledStates"/>
</s:states>

<!-- border --> 
<!--- @private -->
<s:Rect left="0" right="0" top="0" bottom="0" id="border">
    <s:stroke>     
        <!--- @private -->
        <s:SolidColorStroke id="borderStroke" weight="0" />
    </s:stroke>
</s:Rect>

<!-- fill -->
<!--- Defines the appearance of the TextInput component's background. -->
<s:Rect id="background" left="1" right="1" top="1" bottom="1">
    <s:fill>
        <!--- @private Defines the background fill color. -->
        <s:SolidColor id="bgFill" color="#ebebeb" />
    </s:fill>
</s:Rect>

<!-- shadow -->
<!--- @private -->
<s:Rect left="1" top="1" right="1" height="1" id="shadow">
    <s:fill>
        <s:SolidColor color="0x000000" alpha="0.12" />
    </s:fill>
</s:Rect>

<!-- text -->
<!--- @copy spark.components.supportClasses.SkinnableTextBase#textDisplay -->
<s:RichEditableText id="textDisplay"
          verticalAlign="middle"
          widthInChars="10"
          color.normal="#999999"
          color.editing="#29ABE2"
          color.adding="#D67A1E"
          left="1" right="1" top="1" bottom="1" />
<!--- Defines the Label that is used for prompt text. The includeInLayout property is false so the prompt text does not affect measurement. -->
<s:Label id="promptDisplay" maxDisplayedLines="1"
            verticalAlign="middle"
            mouseEnabled="false" mouseChildren="false"
            includeIn="normalWithPrompt,disabledWithPrompt" 
            includeInLayout="false"
            color.normal="#999999"
            color.editing="#29ABE2"
            color.adding="#D67A1E"
            />

I use parent.addEventListener('panel_mode_edit', editMode); for listening the event but it does not work.

What's wrong?

UPDATE:

Dispatching the event:

panelClientes.dispatchEvent(new Event("panel_mode_query", true, true));

Within the TextInput Skin:

    public function init():void {
        hostComponent.addEventListener('panel_mode_edit',   editMode);
        hostComponent.addEventListener('panel_mode_query', queryMode);
        hostComponent.addEventListener('panel_mode_new',    newMode);
    }
    private function editMode(evt:Event):void {
        this.currentState = 'editing';
    }
    private function queryMode(evt:Event):void {
        this.currentState = 'normal';
    }
    private function newMode(evt:Event):void {
        this.currentState = 'adding';
    }

Upvotes: 1

Views: 770

Answers (1)

Josh
Josh

Reputation: 8159

First, you should never listen to the parent or access the parent in any way whatsoever except for logical checks (checking if parent doesn't exist, mainly). In OOP languages, you never want to work up the object chain; you always want to work down it. This is both the proper way to do it and a good exercise to prevent errors.

Now, for the actual problem. The skin is not the object it is being applied to. It is not part of that object. The relation between the two is very very loose. You cannot directly access the skin from the host, but you can directly access the host from the skin.

You can access the component the skin is attached to by using hostComponent. Now, as I said above, you do not want to do hostComponent.parent. For one, the skin is attached before the object is added to the parent (unless you add it manually in AS3 afterward, which I doubt you are doing and is a bad idea anyway). Instead, you want to do this:

  1. Listen for your event in that parent object (the Panel, I believe)
  2. In the handler for that event listener, you can do one of three things:
    1. Set a bindable variable in the child object that you listen to for changes in the skin. This can be done very easily through MXML in the fx:definitions array and in the skin, you listen for that change either in MXML (using {hostComponent.bindableVariableName}) or by listening for the PropertyChangeEvent.PROPERTY_CHANGE event on the hostComponent.
    2. Call a method which sets a different skin altogether. It'd be less compact, but better in terms of abstraction since each skin would be relegated to separate classes. Setting skins after an object is on the stage can be slow if on mobile or a slow desktop, however.
    3. Call a method in the child object which dispatches an event of some kind that you listen for in the skin. This is personally how I have done it in the past. It's clean, OOP-friendly, and as fast or faster than the other 2 options.

Communicating with skins is a royal pain in Flex. This is an intentional design, but sometimes it is needed. Going about it the proper way is essential in creating a efficient app.

Upvotes: 2

Related Questions