13.2.1 声明可绑定数据源--[Bindable]解析
数据绑定的数据源可以是属性变量、方法及ActionScript类对象实例。Flex通过元数据标签[Bindable]进行标识。标识为绑定数据源意味着当数据源变化时,能够调度事件,通知Flex更新目标数据。
Bindable元数据标签的签名如下:
[Bindable] |
或者
[Bindable(event="eventName")] |
开发者一般忽略事件(event)名称,只使用[Bindable]标识可绑定数据源。这种情况下,Flex会默认地创建 mx.events.PropertyChangeEvent类型事件,事件名为propertyChange。当数据绑定的数据源发生变化时,数据源会 自动调度propertyChange事件,通知Flex将新值复制给目标数据。
如果在标识绑定时说明了事件,也就是说采用了[Bindable (event="eventName")]方式,开发者须要自己定义和调度事件。
我们依次说明如何声明属性、ActionScript类和方法作为可绑定的数据源。
声明可绑定属性
在属性变量定义之前,使用元数据标签[Bindable]声明属性可绑定。属性可以是公共属性、私有属性或被保护属性。声明如下:
[Bindable] public var employee:Employee=new Employee(); [Bindable] private var acEmployees:ArrayCollection=new ArrayCollection(); |
如上声明后,employee和acEmployees变量可作为绑定数据源。
对于使用getter和setter方法定义的属性变量,需要在get或set方法前声明[Bindable]。如代码13-3所示。
代码13-3:getter和setter的属性绑定声明
<?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml " layout="vertical" verticalAlign="middle" backgroundColor="white" creationComplete="init();"> <mx:Script> <![CDATA[ private var _prop:String; private function init():void{ prop='bind was triggerd'; } [Bindable] private function set prop(value:String ):void{ //自定义代码 _prop=value; } private function get prop():String{ //自定义代码 return _prop; } ]]> </mx:Script> <mx:Text id="txtDest" text="{prop}"/> <mx:Button label="修改" click="prop='bind was triggerd Again!'"/> </mx:Application> |
声明可绑定ActionScript类
在公共类定义前,使用[Bindable]声明该类是可绑定的。
声明ActionScript类可绑定意味着告诉Flex,这个类的所有公共属性都是可绑定的。因此,代码13-4、代码13-5具有相同的意义。
代码13-4:可绑定ActionScript类一
package com.longfei.bookLabs.bookStore.model { [Bindable] public class Employee { public var name:String; public var title:String; public function Employee(){ } } } |
代码13-5:可绑定ActionScript类二
package com.longfei.bookLabs.bookStore.model { public class Employee { [Bindable] public var name:String; [Bindable] public var title:String; public function Employee(){ } } } |
声明可绑定方法
在Flex中,方法也可以作为数据绑定的数据源,前提条件是方法的参数必须声明为可绑定的属性变量。当属性变量发生变化时,方法就会被调用,并把结果传递给目标数据,如代码13-6所示。
代码13-6:可绑定ActionScript方法
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml " layout="vertical"
verticalAlign="middle" backgroundColor="white">
<mx:Script>
<![CDATA[
private var strDest:String;
private function functionSrc(value:Object):String{
strDest="新数据是: " + value;
return strDest;
}
]]>
</mx:Script>
<mx:Form>
<mx:FormItem label="数据源:">
<mx:TextInput id="txtSrc" />
</mx:FormItem>
<mx:FormItem label="目标数据:">
<mx:TextInput id="txtDest" text="{functionSrc(txtSrc.text)}"/>
</mx:FormItem>
</mx:Form>
</mx:Application>
在Flex编程中,Bindble使用到最多的元数据。该标签可以应用在变量或者类或者方法上。同在在MXML中使用”{}”引用设置为Bindable的变量,从而实现对于变量赋值与界面元素的自动同步。
Bindable的实现采用了GoF的Observer模式 ,或者Publisher/Subscriber模式。该实现允许一个类(或者变量)可以将自身的变化自动通知给感兴趣的对象。
对应Observer模式,GoF的说法是:
定义对象间的一种一对多的依赖关系,当一个对象状态发生改变的时候,所有依赖于他的对象都得到通知并被自动更新。这种交互也成为发布-订阅。目标是通知的发布者。他发布通知是不需要知道谁是他的订阅者(观察者)。可以有任意数目的观察着订阅并接受通知。
在ActionScript中,编译器将会为该标签给特定的属性或类或者方法的变化增加事件关联。
简单的例子,如果定义了变量和一个组件:
[Bindable] var displayName :String;
<mx:TextInput id= "textA" text="{dispayName}"/>,
编译器在后台自动增加事件对应伪代码如下:
function set DispayName(newvar :String) :void { displayName= newvar; this.dispatchEvent(new Event("_dispayNameChanged")); } // 类型初始化时被调用 function MxmlInit(){
textA.addEventListener("_dispayNameChanged")) , UpdateDisplay); } function UpdateDisplay(){ textA.text = displayName; }
可见,编译器在背后做了大量的工作,为我们节省了很多的重复劳动来建立这种观察关系。
对于初学Flex的人,对于bindable的误解往往是认为Bindable是双向的数值链接,事实上Observer模式对应的一个要素就是被观察者对观察者一无所知。
比如上面的例子,对于textA的修改,其值并不会自动的体现在displayName上。Bindable只是建立单项的观察者的关系,这一点至关重要。
对应MVC架构中,被观察者往往对应Model(数据模型?)的角色,而观察者则对应于View(视图)的角色,事实上很多的MVC架构也是非常依赖于Bindable这个工具。
那么什么情况下应该应用Bindable?Leaf的个人经验和理解是:
注意Bindable是有额外的开销的 ,别忘了那些编译器自动生成的代码,如果需要复杂的逻辑(并非简单的数值赋值)对应的时候,不要使用bindable。
其实也是因为这一点,ActionScript中,即使指定了Bindable,如果对应的set或者get中逻辑比较复杂,则不会自动的创建事件关联操作,而是忽略Bindable标签。
可以自己使用Bindable[事件名称]的方式建立关联 ,Adobe给出了这样的例子:
private var _maxFontSize:Number = 15; [Bindable(event="maxFontSizeChanged")] // Define public getter method. public function get maxFontSize():Number { return _maxFontSize; } // Define public setter method. public function set maxFontSize(value:Number):void { if (value <= 30) { _maxFontSize = value; } else { _maxFontSize = 30; } // Create event object. var eventObj:Event = new Event("maxFontSizeChanged"); dispatchEvent(eventObj); }
注意在这个例子里,复杂的逻辑是在setter里面做的,而不是getter,为什么? 因为setter调用的次数在正常的情况下总是要比getter少,原则上,
复杂的逻辑通常在赋值时完成,用空间换时间,而不是在每一次取值时计算。
特别信息:
在Flex4里面,新增加了允许对于表达式的观察! 例如:
<mx:Button enabled="{state == 'ready'}"/>
同样的代码在Flex3中无效。
附,Flex3中对于Bindable的解释的LiveDoc 链接如下 。