Flex 4自定义组件 - 如何通知皮肤属性变化?

时间:2012-01-30 15:36:33

标签: flex flex4

我正在尝试制作一个自定义Flex 4+组件,让皮肤了解自定义属性的更改。此属性将确定按钮上的图形(以及其他一些可视更改),但数据将不断更改,因为它将由计时器更新。

我看过无数的例子,但似乎仍然无法正确理解语法或发现事物应该如何分离。我已经看过覆盖commitProperties和PropertyChangeEvent而没有成功。所以我有两个问题。

1)如何在绑定属性发生变化时通知皮肤?

2)如果组件的绑定属性的数据是对象,如果对象的属性发生更改(或者更好地单独传递每个属性),绑定是否会正常工作?

这是我正在努力实现的一个精简的例子。

该组件如下所示:

<s:ButtonBase xmlns:fx="http://ns.adobe.com/mxml/2009"
          xmlns:s="library://ns.adobe.com/flex/spark"
          xmlns:mx="library://ns.adobe.com/flex/mx">

<fx:Script>
    <![CDATA[
        private var _iconData:String;

        [Bindable]
        public function get iconData():String
        {
            return _iconData;
        }
        public function set iconData(value:String):void
        {
            _iconData = value;
        }
    ]]>
</fx:Script>

我这样称呼它:

<components:MyButton id="myButton" iconData="{myData.curIconTag}" skinClass="skins.MyButtonSkin" />

我有很多不同的图像我可以加载,所以我担心状态的数量(上/下/上/禁用等组合可能会失控,所以SetIconDisplay正在设置图标,但真正的关键是我在该函数中有其他代码需要在每隔X分钟左右更改一次iconData属性时执行。所以皮肤是这样的:

<s:Skin xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    xmlns:fb="http://ns.adobe.com/flashbuilder/2009"
    creationComplete="init()">

<fx:Metadata>
    [HostComponent("components.MyButton")]
</fx:Metadata>

<s:states>
    <s:State name="default" />
    <s:State name="down"/>
    <s:State name="up"/>
    <s:State name="over"/>
    <s:State name="disabled" />
</s:states>

<fx:Script>
    <![CDATA[
        import components.MyButton;

        [Embed(source="images/image1.png")]
        private var icon1:Class;

        [Embed(source="images/image2.png")]
        private var icon2:Class;

        [Embed(source="images/image3.png")]
        private var icon3:Class;

        [Bindable] 
        public var hostComponent:MyButton;

        [Bindable] 
        private var iconClass:Class;

        private function init():void
        {
            iconClass = new Class();
        }

        // how do I get this called when the iconData property on my custom component is changed?
        private function SetIconDisplay():void
        {
            switch (hostComponent.iconData)
            {
                case "apple":
                    iconClass=icon1;
                    break;
                case "orange":
                    iconClass=icon2;
                    break;
                case "grape":
                    iconClass=icon3;
                    break;
            }
        }
    ]]>        
</fx:Script>

<s:BitmapImage source="{iconClass}" x="0" y="0" width="180" height="108"/>

同样,不要太担心皮肤实际上正在做什么,因为它可能会改变(不使用状态)。我只是试图找出在绑定属性发生变化时如何调用特定函数。

谢谢!

4 个答案:

答案 0 :(得分:5)

我最终在更新数据时调度自定义事件并在皮肤中进行侦听。

组件:

<s:ButtonBase xmlns:fx="http://ns.adobe.com/mxml/2009" 
        xmlns:s="library://ns.adobe.com/flex/spark" 
        xmlns:mx="library://ns.adobe.com/flex/mx">

 <fx:Script>
      <![CDATA[
          import classes.CustomEvent;

          private var _iconData:String;

          [Bindable]
          public function get iconData():String
          {
               return _iconData;
          }
          public function set iconData(value:String):void 
          {
               _iconData = value;
               dispatchEvent(new CustomEvent("iconDataUpdated"));
          }
      ]]>
 </fx:Script>

皮肤补充说:

    protected function skin_preinitializeHandler(event:FlexEvent):void
{
        hostComponent.addEventListener(CustomEvent.ICON_DATA_UPDATED,SetIconDisplay);
}

答案 1 :(得分:3)

让基类在一个特定的皮肤上调用一个函数可能会变得很尴尬,因为这意味着基类依赖于皮肤类,这使得换出皮肤变得很困难。有两种好方法可以解决这个问题:

选项1 :将iconClass向上移动到组件中。外观类可以直接绑定到属性,并且决定使用哪个图标的逻辑可以由组件而不是外观处理。这样可以保持逻辑不受皮肤影响,并保持必须使用的皮肤代码量。

选项2 :向外观添加iconData属性,并将其绑定到主机组件的iconData属性。在setter函数中,当您具有有效值时,请调用SetIconDisplay。这样可以将图标封装在皮肤中,如果您想为同一个组件使用非常不同的皮肤,这可能会有所帮助。

编辑:如果您计划创建其他几个不使用图标的皮肤,那么#2就是您的选择。像这样在皮肤上创建一个属性:

private var _iconData:String;

public function get iconData():String
{
    return _iconData;
}

public function set iconData(value:String):void
{
    _iconData = value;
    SetIconDisplay()
}

然后使用绑定将其连接到hostComponent:

<fx:Binding source="hostComponent.iconData" destination="iconData" />

答案 2 :(得分:1)

一般问题的另一个解决方案,虽然在这种情况下可能不理想,但是只要属性发生变化,就调用skin.invalidateDisplayList()。然后,在皮肤中,覆盖updateDisplayList函数,并从那里调用一个函数来响应已更改的属性,以及显然调用父类的函数。

见这里:https://forums.adobe.com/thread/797247

答案 3 :(得分:0)

<s:BitmapImage source="{hostComponent.iconClass}" />

应该有效


您不需要声明 public var hostComponent:MyButton;

它是SparkSkin的一部分