`
vip_chenlin
  • 浏览: 7378 次
  • 性别: Icon_minigender_1
  • 来自: 西安
最近访客 更多访客>>
文章分类
社区版块
存档分类
最新评论

Flex 数据绑定易犯的错误:普遍的误用和错误

阅读更多

数据绑定(data binding)- 在创建Flex或Aodbe AIR应用程序中,最常用而且很有用的是将数据从一个对象传递给另外一个对象。于此同时,若开发者不完全理解它的机制的话,可能会给程序造成初始化缓慢或失败的问题。在必须使用的时候,正确地使用数据绑定无疑是好的办法。在这篇文章中,我列出了10个最易犯的错误,以便开发者在创建应用中合理地使用数据绑定。

错过无声的错误

有一种情况,就是你使用了数据绑定,但最终的结果不是你所期待的,没有看到绑定的效果;而且也没有错误提示。

绑定表达式或绑定的函数里抛出的异常和错误,被绑定框架所捕获,这种叫无声捕获。最终导致的结果就是,你通过Flash Player的debug版本去调试程序时,不能在运行时看到异常信息。不仅绑定功能没有工作,连错误都没显示出来。

为什么会有这种无声捕获呢?

在绑定情景出现前,代码需要满足绑定机制所必须的条件。绑定机制可以吞咽任何错误以阻止在运行时抛出异常信息。这样可以避免出现哪些不必要的错误提示信息。

观察并考虑下面这个简单的绑定例子:

view plaincopy to clipboardprint?
01.<?xml version="1.0" encoding="utf-8"?>  
02.<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"   
03.               xmlns:s="library://ns.adobe.com/flex/spark"   
04.               xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600" 
05.               preinitialize="preinitializeHandler(event)" 
06.               >  
07.    <fx:Script>  
08.        <!--[CDATA[  
09.            import mx.events.FlexEvent;  
10.              
11.            [Bidable]  
12.            private var xml:XML =   
13.                <users>  
14.                    <user>  
15.                        <name>EladElrom</name>  
16.                        <address>1 Wall Street</address>  
17.                    </user>  
18.                </users>;  
19.              
20.            protected function preinitializeHandler(event:FlexEvent):void 
21.            {  
22.                xml = null;  
23.            }  
24.        ]]-->  
25.    </fx:Script>  
26.    <s:Label id="label" text="{xml.user.name}" />  
27.</s:Application> 
<?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"
      xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600"
      preinitialize="preinitializeHandler(event)"
      >
 <fx:Script>
  <!--[CDATA[
   import mx.events.FlexEvent;
   
   [Bidable]
   private var xml:XML =
    <users>
     <user>
      <name>EladElrom</name>
      <address>1 Wall Street</address>
     </user>
    </users>;
   
   protected function preinitializeHandler(event:FlexEvent):void
   {
    xml = null;
   }
  ]]-->
 </fx:Script>
 <s:Label id="label" text="{xml.user.name}" />
</s:Application>
 

我已把xml变量绑定到一个Label控件上了。代码看上去没有任何错误,但是,我在预初始化程序时把变量xml的值赋为了null。当初始化这个Laberl 控件时,没有任何设置。因为变量xml为空,所以xml对象的name属性也为空。当你运行程序时,你会看到界面没有任何显示,也没有异常错误,这时的错误信息被悄悄地捕获了。

Debugging bindings

尽管这些错误信息被悄悄地捕获了,但还是可以查出它们在哪里被捕获了。在BindingManager.as和Binding.as代码中,我们可以跟踪到它,但首先你必须安装了完整的Flex SDK.(见图1)

 

相反,你可以设置断点和钻取绑定对象相关的属性值,查明是什么错误。在这种情况下,你会发现XML 对象的值被设置为null,导致了绑定失败。

另外一个解决办法更直观,就是运用BindingManger 类里的debugBinding方法。设置你所要看到的控件和属性,然后你能看到错误信息如何被解雇和捕获的。

在上面的例子代码中,我注释了BindingManager.debugBinding("label.text");这句代码。

放开注释,重新调试运行;可以在控制台栏里查看错误信息。(见图2)

 

看一看 Binding.as和BindingManager.as类,会发现代码里有好多if 和 try... catch 语句,这些语句确保是否是有效的绑定。下面给出在使用绑定时的错误信息会被悄悄地捕获:

 

 

Error #1006: Call attempted on an object that is not a function.

Error #1009: null has no properties.

Error #1010: undefined has no properties.

Error #1055: - has no properties.

Error #1069: Property - not found on - and there is no default value

如果会出现上面任何一个错误,绑定管理者会悄悄地捕获,绑定就起不了作用。

 


运行时错误在大多数场景下会正常地出现。比如,当含有绑定表达式的Label控件初始化时,目标对象的属性没有没赋值或表达式不正确,导致发生了1009的错误信息。同样地,一些绑定表达式只有在应用程序发生动作时才起作用。比如你将数据绑定到一个List 控件的 selectedItem 属性上,只有当你选择item时才起到绑定的作用。

试图绑定到一个没有实现IPropertyChangeNotifier接口的类上

实现了IPropertyChangeNotifier接口的类,当类的属性发生改变时,都会派发事件。举个例子,你可以查看UIComponent 类的代码。实际上UIComponent 类实现了,当属性一旦改变都会派发dispatchPropertyCangeEvent事件。

现在来查看下面这个用户信息的类:

view plaincopy to clipboardprint?
01.package vo  
02.{  
03.   public final class UserInfo  
04.   {  
05.      public var userName:String;  
06.      public var password:String;  
07.      public function UserInfo()  
08.      {  
09.      }  
10.   }  
11.} 
package vo
{
   public final class UserInfo
   {
      public var userName:String;
      public var password:String;
      public function UserInfo()
      {
      }
   }
}

当你试图将Label控件的text属性绑定到某个UserIfo类的属性上时,正如下面的代码一样,绑定不起作用。

view plaincopy to clipboardprint?
01.<?xml version="1.0" encoding="utf-8"?>  
02.<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"      
03.   xmlns:s="library://ns.adobe.com/flex/spark"   
04.   xmlns:mx="library://ns.adobe.com/flex/mx"   
05.   minWidth="1024" minHeight="768"   
06.   creationComplete="creationCompleteHandler()">  
07.   <fx:Script>  
08.      <!--[CDATA[    
09.         import vo.UserInfo;   
10.         [Bindable]  
11.         private var userInfo:UserInfo = new UserInfo();             
12.                          
13.         protected function creationCompleteHandler():void 
14.         {   
15.            userInfo.userName = "EladElrom";  
16.         }   
17.      ]]-->  
18.   </fx:Script>  
19.   <s:Label id="lbl" text="{userInfo.userName}" />  
20.</s:Application> 
<?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"
   xmlns:mx="library://ns.adobe.com/flex/mx"
   minWidth="1024" minHeight="768"
   creationComplete="creationCompleteHandler()">
   <fx:Script>
      <!--[CDATA[ 
         import vo.UserInfo;
         [Bindable]
         private var userInfo:UserInfo = new UserInfo();          
                       
         protected function creationCompleteHandler():void
         {
            userInfo.userName = "EladElrom";
         }
      ]]-->
   </fx:Script>
   <s:Label id="lbl" text="{userInfo.userName}" />
</s:Application>

因为上面的代码试图绑定到一个没有实现IPropertyChangeNotifier接口的类上,绑定机制不能工作。

在这种情况下,在IDE的Problems视图窗口中会看到如下信息:

 
data binding will not be able to detect assignments to "userName".

 

在上面这个例子中,你可以通过给UserIfno类添加一个[Bindable]标签来解决绑定不工作的问题。这样就可以确保这个类的公有属性都可以被绑定。Flex编译器会生成相应的一对公有的getter和setter构造器,来确保它们满足绑定机制的要求。或者,若不希望所有属性可被绑定,可以将[Bindable]标签添加到类的指定属性上。

view plaincopy to clipboardprint?01.package vo   02.{   03.   [Bindable]   04.   public final class UserInfo   05.   {   06.      public var userName:String;   07.      public var password:String;   08.                                09.      public function UserInfo()    10.      {   11.      }   12.   }   13.}  package vo
{
   [Bindable]
   public final class UserInfo
   {
      public var userName:String;
      public var password:String;
                            
      public function UserInfo()
      {
      }
   }
}

ObjectProxy 类

 


如果一个类要想使用数据绑定的功能,必须得实现IPropertyChangeNotifier接口;否则,这个对象不能够绑定的。

但是,有一些类的属性或变量,比如简单的变量,它们不能使用[Bindable]标签,也不能实现必要的接口。也就是说,

这个类属于你创建的,你可以添加[Bindable]来轻松实现绑定的功能;但若这个类不属于你,而你又想实现绑定的功能,

这时你就可以考虑使用ObjectProxy类。ObjectProxy 类包装一个非绑定类和一个属性改变就会派发的PropertyChangeEvent事件。

下面是ObjectProxy的应用例子。创建一个ObjectProxy类对象,并用非绑定类来实例化ObjectProxy,在这里我们用UserInfo这个类。

接着给实例化对象添加一个PropertyChangeEvent事件的侦听函数,并跟踪UserInfo某个条目的改变。

view plaincopy to clipboardprint?
01.version="1.0" encoding="utf-8"?>            
02.<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"              
03.   xmlns:s="library://ns.adobe.com/flex/spark" 
04.   xmlns:mx="library://ns.adobe.com/flex/mx" 
05.   minWidth="1024" minHeight="768"                                      
06.   creationComplete="creationCompleteHandler()">  
07.     
08.   <fx:Script>          
09.      <!--[CDATA[   
10.         import mx.events.PropertyChangeEvent;  
11.         import mx.utils.ObjectProxy;  
12.         import vo.UserInfo;  
13.         private var userInfo:UserInfo = new UserInfo();  
14.         private var objectProxy:ObjectProxy;  
15.         protected function creationCompleteHandler():void 
16.         {  
17.            objectProxy = new ObjectProxy( userInfo );  
18.            objectProxy.addEventListener( PropertyChangeEvent.PROPERTY_CHANGE, onPropertyChange );  
19.            objectProxy.userName = "EladElrom";  
20.            objectProxy.password = "123";  
21.         }   
22.         private function onPropertyChange( event:PropertyChangeEvent ):void    
23.         {      
24.            lbl.text = event.newValue.toString();  
25.         }      
26.      ]]-->         
27.   </fx:Script>    
28.               
29.   <s:Label id="lbl" />   
30.     
31.</s:Application> 
version="1.0" encoding="utf-8"?>         
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"           
   xmlns:s="library://ns.adobe.com/flex/spark"
   xmlns:mx="library://ns.adobe.com/flex/mx"
   minWidth="1024" minHeight="768"                                   
   creationComplete="creationCompleteHandler()">
  
   <fx:Script>       
      <!--[CDATA[
         import mx.events.PropertyChangeEvent;
         import mx.utils.ObjectProxy;
         import vo.UserInfo;
         private var userInfo:UserInfo = new UserInfo();
         private var objectProxy:ObjectProxy;
         protected function creationCompleteHandler():void
         {
            objectProxy = new ObjectProxy( userInfo );
            objectProxy.addEventListener( PropertyChangeEvent.PROPERTY_CHANGE, onPropertyChange );
            objectProxy.userName = "EladElrom";
            objectProxy.password = "123";
         }
         private function onPropertyChange( event:PropertyChangeEvent ):void 
         {   
            lbl.text = event.newValue.toString();
         }   
      ]]-->      
   </fx:Script> 
            
   <s:Label id="lbl" />
  
</s:Application>

其中ObjectProxy类时,有一个需要你注意的就是,为了方便通知的派发,每当目标对象的任何对象改变,都会调用事件注册的侦听者。

这样有时显得过了头,例如在上面的例子中,onPropertyChange 事件是被调用了两次。

使用绑定代替直接赋值

在某种情况下不需要使用绑定,完全可以使用直接赋值来达到同样的效果,这时最好要避免使用绑定。我看到过各种各样这种类型的错误。

下面的代码是是其中一个例子:

view plaincopy to clipboardprint?
01.<?xml version="1.0" encoding="utf-8"?>  
02.<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009
03.   xmlns:s="library://ns.adobe.com/flex/spark" 
04.   minWidth="1024" minHeight="768">                          
05.   <fx:Script>  
06.      <!--[CDATA[  
07.         private var text:String;  
08.      ]]-->  
09.   </fx:Script>                          
10.   <s:layout>  
11.      <s:VerticalLayout/>  
12.   </s:layout>  
13.                             
14.   <s:TextInput id="textInput2" text="{text}"  />  
15.</s:Application> 
<?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"
   minWidth="1024" minHeight="768">                       
   <fx:Script>
      <!--[CDATA[
         private var text:String;
      ]]-->
   </fx:Script>                       
   <s:layout>
      <s:VerticalLayout/>
   </s:layout>
                          
   <s:TextInput id="textInput2" text="{text}"  />
</s:Application>

代码中将TextInput控件的text属性绑定到一个私有变量text上。它看起来没有什么错误,对吧?我在Flex应用程序中经常看到这样

的代码。你会发现尽管这个私有的文本型text变量只需要赋值一次,但Flex 编译器会按照绑定机制自动生成相关的代码。另外,还有一些情况,在赋值后取消绑定或删除绑定代码以减少开销,但你不能在MXML类中使用<mx:Binding>标签。

根据经验,避免绑定到一个私有的变量。

在上面的例子中,你可以直接赋值:

view plaincopy to clipboardprint?
01.<s:TextInput id="textInput2" text="some text goes here" /> 
<s:TextInput id="textInput2" text="some text goes here" />

当使用直接赋值,你可以有效地减少内存的开销,因为编译器就不用产生不必要的绑定代码了。

经验来说,使用数据绑定时,要绑定到一个会改变而且将要改变的属性上。

忘记取消绑定或导致内存泄露

你可以在MXML类中使用<mx:Binding>标签来轻易地实现绑定的功能,但是这样的解决方法可能会带来内存开销过多。

另外就是,使用这样子的绑定方法,将来很难取消绑定。如果要将程序优化成一个高性能标准,你可以使用BindignUtils类

来进行绑定你的对象。关于BindingUtils类有两种方式:

<>bindProperty():是一个静态的方法,可用于绑定一个公有的属性。

<>bindSetter():是一个静态的方法,胜于绑定一个函数。

来看一看bindProerty方法的声明:

view plaincopy to clipboardprint?
01.public static function bindProperty(  
02.     site:Object, prop:String,  
03.     host:Object, chain:Object,  
04.     commitOnly:Boolean = false,  
05.     useWeakReference:Boolean = false):ChangeWatcher 
public static function bindProperty(
     site:Object, prop:String,
     host:Object, chain:Object,
     commitOnly:Boolean = false,
     useWeakReference:Boolean = false):ChangeWatcher

site,host参数分别表示各自的目标和源对象。如果仅在提交 change 事件时需要调用处理函数,则设置为 true;如果无论

是否提交 change 事件都调用处理函数,则设置为 false。

useWeakReference参数相对于host来说是强引用还是弱引用。强引用(默认的)能阻止垃圾对host的回收,弱引用不能。

下面这个例子包含一个文本框和一个Label控件。当TextInput控件在preinitialized阶段时,preinitializeHandler()方法

被调用,里面用到了bindProperty这种方式。

view plaincopy to clipboardprint?
01.<?xml version="1.0" encoding="utf-8"?>  
02.<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009
03.   xmlns:s="library://ns.adobe.com/flex/spark" 
04.   xmlns:mx="library://ns.adobe.com/flex/mx" 
05.   minWidth="1024" minHeight="768">  
06.      <fx:Script>  
07.         <!--[CDATA[  
08.            import mx.binding.utils.BindingUtils;  
09.            import mx.events.FlexEvent;  
10.                                    
11.            protected function preinitializeHandler(event:FlexEvent):void 
12.            {  
13.               BindingUtils.bindProperty(label, "text", textInput, "text");  
14.            }                                    
15.         ]]-->  
16.      </fx:Script>  
17.      
18.      <s:layout>  
19.         <s:VerticalLayout/>  
20.      </s:layout>       
21.                     
22.      <s:TextInput id="textInput" preinitialize="preinitializeHandler(event)" />  
23.      <s:Label id="label" />    
24.   
25.</s:Application> 
<?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"
   xmlns:mx="library://ns.adobe.com/flex/mx"
   minWidth="1024" minHeight="768">
      <fx:Script>
         <!--[CDATA[
            import mx.binding.utils.BindingUtils;
            import mx.events.FlexEvent;
                                 
            protected function preinitializeHandler(event:FlexEvent):void
            {
               BindingUtils.bindProperty(label, "text", textInput, "text");
            }                                 
         ]]-->
      </fx:Script>
   
      <s:layout>
         <s:VerticalLayout/>
      </s:layout>    
                  
      <s:TextInput id="textInput" preinitialize="preinitializeHandler(event)" />
      <s:Label id="label" /> 
 
</s:Application>

下面是bindSetter方式的定义:

view plaincopy to clipboardprint?
01.public static function bindSetter(setter:Function, host:Object,  
02.   chain:Object,  
03.   commitOnly:Boolean = false,  
04.   useWeakReference:Boolean = false):ChangeWatcher 
public static function bindSetter(setter:Function, host:Object,
   chain:Object,
   commitOnly:Boolean = false,
   useWeakReference:Boolean = false):ChangeWatcher

setter参数为回调函数,当源host的chain的值改变时,都会触发这个回调函数。

下面就是利用bindSetter来进行绑定的一个例子:

view plaincopy to clipboardprint?
01.<?xml version="1.0" encoding="utf-8"?>  
02.<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"   
03.   xmlns:s="library://ns.adobe.com/flex/spark"   
04.   xmlns:mx="library://ns.adobe.com/flex/mx"   
05.   minWidth="1024" minHeight="768">      
06.                          
07.   <fx:Script>  
08.      <!--[CDATA[  
09.         import mx.binding.utils.BindingUtils;  
10.         import mx.events.FlexEvent;  
11.         protected function preinitializeHandler(event:FlexEvent):void 
12.         {  
13.            BindingUtils.bindSetter(bindingSetter, textInput, "text");   
14.         }   
15.                                   
16.         private function bindingSetter(str:String):void 
17.         {  
18.            label.text = str;   
19.         }                                    
20.      ]]-->  
21.   </fx:Script>   
22.                         
23.   <s:layout>  
24.      <s:VerticalLayout/>  
25.   </s:layout>   
26.                         
27.   <s:TextInput id="textInput" preinitialize="preinitializeHandler(event)" />  
28.   <s:Label id="label" />  
29.                          
30.</s:Application> 
<?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"
   xmlns:mx="library://ns.adobe.com/flex/mx"
   minWidth="1024" minHeight="768">   
                     
   <fx:Script>
      <!--[CDATA[
         import mx.binding.utils.BindingUtils;
         import mx.events.FlexEvent;
         protected function preinitializeHandler(event:FlexEvent):void
         {
            BindingUtils.bindSetter(bindingSetter, textInput, "text");
         }
                                
         private function bindingSetter(str:String):void
         {
            label.text = str;
         }                                 
      ]]-->
   </fx:Script>
                      
   <s:layout>
      <s:VerticalLayout/>
   </s:layout>
                      
   <s:TextInput id="textInput" preinitialize="preinitializeHandler(event)" />
   <s:Label id="label" />
                       
</s:Application>

在幕后,ChangeWatcher类被用在BindingUtils 类里。它允许使用弱引用,所以当你将host

的useWeakReference 值设为true时,垃圾回收器会自动地进行回收,避免内存的泄露。

当使用弱引用,并且不再使用绑定功能情况下。这时ChangeWatcher 需要设置成

unwatched的状态。为了确保内存不被浪费,这项任务是必须的。如下面的例子:

view plaincopy to clipboardprint?
01.<?xml version="1.0" encoding="utf-8"?>  
02.<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009
03.   xmlns:s="library://ns.adobe.com/flex/spark" 
04.   xmlns:mx="library://ns.adobe.com/flex/mx" 
05.   minWidth="1024" minHeight="768">  
06.   <fx:Script>  
07.      <!--[CDATA[  
08.         import mx.binding.utils.ChangeWatcher;  
09.         import mx.binding.utils.BindingUtils;  
10.         import mx.events.FlexEvent;             
11.         private var change:ChangeWatcher;     
12.         protected function preinitializeHandler(event:FlexEvent):void 
13.         {  
14.            change = BindingUtils.bindProperty( label, "text",  
15.            textInput, "text", false, true);  
16.         }                                    
17.         protected function clickHandler(event:MouseEvent):void 
18.         {  
19.            change.unwatch();  
20.            change = null;  
21.         }                                    
22.      ]]-->  
23.   </fx:Script>   
24.                         
25.   <s:layout>  
26.      <s:VerticalLayout/>  
27.   </s:layout>  
28.   
29.   <s:TextInput id="textInput" 
30.      preinitialize="preinitializeHandler(event)" />  
31.   <s:Label id="label" />    
32.                        
33.   <s:Button label="Stop binding" click="clickHandler(event)" />  
34.</s:Application> 
<?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"
   xmlns:mx="library://ns.adobe.com/flex/mx"
   minWidth="1024" minHeight="768">
   <fx:Script>
      <!--[CDATA[
         import mx.binding.utils.ChangeWatcher;
         import mx.binding.utils.BindingUtils;
         import mx.events.FlexEvent;          
         private var change:ChangeWatcher;  
         protected function preinitializeHandler(event:FlexEvent):void
         {
            change = BindingUtils.bindProperty( label, "text",
            textInput, "text", false, true);
         }                                 
         protected function clickHandler(event:MouseEvent):void
         {
            change.unwatch();
            change = null;
         }                                 
      ]]-->
   </fx:Script>
                      
   <s:layout>
      <s:VerticalLayout/>
   </s:layout>
 
   <s:TextInput id="textInput"
      preinitialize="preinitializeHandler(event)" />
   <s:Label id="label" /> 
                     
   <s:Button label="Stop binding" click="clickHandler(event)" />
</s:Application>

将TestInput的text属性绑定到Label控件的text属性上,一旦你在TextInput控件输入任何

文字时,数据会拷贝到Label控件的text属性上。当你准备取消绑定时,点击'Stop Binding'

按钮。这时不再观察属性的改变,将对象设置为null以便于进行垃圾回收。

不改变默认的事件常量-propertyChange

在没有添加事件设置使用Bindable标签来进行绑定时,propertyChange作为默认的事件类型被派发。

所以一个[Bindable]标签相当于Bindable(event="propertyChange")这句话。当你没有特别指明事件的

类型时,编译器会额外创建它的setter和getter构造器代码;所以,你要定义自己的常量以免造成过多

的内存浪费。

细想下面的表达式:

view plaincopy to clipboardprint?
01.[Bindable]  
02.public var testData:String; 
[Bindable]
public var testData:String;

因没有设置事件的名称,编译器会将它编译成如下代码:

view plaincopy to clipboardprint?
01.[Bindable(event="propertyChange")]           
02.public function get testData():String   
03.{   
04.   return this._1147269284testData;   
05.}   
06.public function set testData(value:String):void   
07.{   
08.   var oldValue:Object = this._1147269284testData;  
09.   if (oldValue !== value) {   
10.      this._1147269284testData = value;  
11.      if (this.hasEventListener("propertyChange"))                               
12.         this.dispatchEvent(mx.events.PropertyChangeEvent.createUpdateEvent  
13.         (this, "testData", oldValue, value));   
14.      }   
15.} 
[Bindable(event="propertyChange")]        
public function get testData():String
{
   return this._1147269284testData;
}
public function set testData(value:String):void
{
   var oldValue:Object = this._1147269284testData;
   if (oldValue !== value) {
      this._1147269284testData = value;
      if (this.hasEventListener("propertyChange"))                            
         this.dispatchEvent(mx.events.PropertyChangeEvent.createUpdateEvent
         (this, "testData", oldValue, value));
      }
}

正如你所看到的,编译器生成的setter构造器中有派发PropertyChangeEvent事件的

代码。当你改变默认的事件名称时,Flex编译器将不再生成额外的代码,这时你要为事件

派发负责了。这样能够给你可以优化侦听者代码。若你不改变默认的设置,每一个使用

[Bindable]的属性,都会派发PropertyChangeEvent事件;而它们的侦听者都会响应。

在一个大量使用[Bindable]属性的类中,这个过程是很占用内存的。

一旦你把事件声称了自己的,比如下面例子,编译器在编译时只是做一下拷贝。

view plaincopy to clipboardprint?
01.Private var _number:Number = 0;       
02.   
03.[Bindable(event="changeNumberEvent")]   
04.public function get number():Number     
05.{                         
06.   return _number;  
07.}               
08.            
09.public function set number(value:Number) : void   
10.{                         
11.   _number = value;  
12.   dispatchEvent(new Event("changeNumberEvent"));  
13.} 
Private var _number:Number = 0;    
 
[Bindable(event="changeNumberEvent")]
public function get number():Number  
{                      
   return _number;
}            
         
public function set number(value:Number) : void
{                      
   _number = value;
   dispatchEvent(new Event("changeNumberEvent"));
}

使用错误Bindable事件的名称

在[Bindable]标签中使用错误的事件名称,导致绑定不起作用,而且你可能还不知道这是为什么。当你在使用

[Bidnable]标签中自定义的事件名称,下面的例子好像是很好的代码:

view plaincopy to clipboardprint?
01.public static const EVENT_CHANGED_CONST:String = "eventChangedConst";   
02.   
03.private var _number:Number = 0;   
04.   
05.[Bindable(event=EVENT_CHANGED_CONST)]   
06.public function get number():Number    
07.{                            
08.   return _number;  
09.}      
10.public function set number(value:Number) : void   
11.{                            
12.   _number = value;  
13.   dispatchEvent(new Event(EVENT_CHANGED_CONST));  
14.} 
public static const EVENT_CHANGED_CONST:String = "eventChangedConst";
 
private var _number:Number = 0;
 
[Bindable(event=EVENT_CHANGED_CONST)]
public function get number():Number 
{                         
   return _number;
}   
public function set number(value:Number) : void
{                         
   _number = value;
   dispatchEvent(new Event(EVENT_CHANGED_CONST));
}

上面的代码是将一个静态常量赋值给事件的名称,并使用相同的值去派发事件。但是,当

number值改变时,绑定没有工作。原因是事件的名称是EVENT_CHANGED_CONST,而不是

这个常量的值。

所以将代码改成如下形式,才是正确的:

view plaincopy to clipboardprint?
01.public static const EVENT_CHANGED_CONST:String = "eventChangedConst";     
02.   
03.private var _number:Number = 0;     
04.   
05.[Bindable(event="eventChangedConst")]   
06.public function get number():Number     
07.{                         
08.   return 
09.   _number;  
10.}  
11.public function set number(value:Number) : void   
12.{                         
13.   _number = value;  
14.   dispatchEvent(new Event(EVENT_CHANGED_CONST));  
15.} 
public static const EVENT_CHANGED_CONST:String = "eventChangedConst";  
 
private var _number:Number = 0;  
 
[Bindable(event="eventChangedConst")]
public function get number():Number  
{                      
   return
   _number;
}
public function set number(value:Number) : void
{                      
   _number = value;
   dispatchEvent(new Event(EVENT_CHANGED_CONST));
}

假定绑定的执行顺序

一个普遍的错误是在绑定时,假定绑定的发生是按照一定的顺序来发生。这样可能导致你的程序发生警告提示

和绑定不起作用。ActionScript中的事件是异步执行的方式。

看一下面这个例子:

view plaincopy to clipboardprint?
01.<?xml version="1.0" encoding="utf-8"?>  
02.<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009
03.   xmlns:s="library://ns.adobe.com/flex/spark" 
04.   xmlns:mx="library://ns.adobe.com/flex/mx" 
05.   minWidth="1024" minHeight="768" 
06.   creationComplete="creationCompleteHandler(event)">                          
07.   <fx:Script>  
08.      <!--[CDATA[  
09.         import mx.events.FlexEvent;  
10.                                    
11.         [Bindable]  
12.         public var buttonText:String = "Execution order mistake ";                                    
13.      ]]-->  
14.   </fx:Script>  
15.                          
16.   <s:layout>  
17.      <s:HorizontalLayout />  
18.   </s:layout>  
19.                          
20.   <s:Label id="label" text="{buttonText}" />  
21.   <s:Button label="{label.text}" />  
22.                          
23.</s:Application> 
<?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"
   xmlns:mx="library://ns.adobe.com/flex/mx"
   minWidth="1024" minHeight="768"
   creationComplete="creationCompleteHandler(event)">                       
   <fx:Script>
      <!--[CDATA[
         import mx.events.FlexEvent;
                                 
         [Bindable]
         public var buttonText:String = "Execution order mistake ";                                 
      ]]-->
   </fx:Script>
                       
   <s:layout>
      <s:HorizontalLayout />
   </s:layout>
                       
   <s:Label id="label" text="{buttonText}" />
   <s:Button label="{label.text}" />
                       
</s:Application>

上面的代码可能会工作,但有时也不能工作。在Button的label属性绑定到Label控件的的text属性上时,

假定Label的text属性值已被改变了。当你编译这个程序时,编译器会给出一个警告信息。

下面是另外一个例子,它假定第一个TextInput控件已存在。这种赋值方式会生成绑定所需要的全部代码。

你必须决定这种方式或直接将(y=35)哪个是比较好的方式。

view plaincopy to clipboardprint?
01.<?xml version="1.0" encoding="utf-8"?>  
02.<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009
03.   xmlns:s="library://ns.adobe.com/flex/spark" 
04.   xmlns:mx="library://ns.adobe.com/flex/mx" 
05.   minWidth="1024" minHeight="768">  
06.                          
07.   <s:TextInput id="textInput1" x="10" y="0" />  
08.   <s:TextInput id="textInput2" x="0" y="{textInput1.x+25}" />  
09.                          
10.</s:Application> 
<?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"
   xmlns:mx="library://ns.adobe.com/flex/mx"
   minWidth="1024" minHeight="768">
                       
   <s:TextInput id="textInput1" x="10" y="0" />
   <s:TextInput id="textInput2" x="0" y="{textInput1.x+25}" />
                       
</s:Application>

用绑定代替事件

在多数情况下你可以轻松地使用事件来代替绑定给一个对象进行赋值。你可以使用组件生命周期中的事件或

重写组件的方法如childrenCreated()和initializationComplete()来进行赋值。另外,你可以使用ChangeWatcher

去侦听一个数据的改变,这样做是低耦合的。在使用ChangeWatcher时,多数情况下若能手动地得到数据更改的通知,

应避免使用ChangejWatcher。例如,所有Flex的集合对象都会广播 collectionChange事件,这时你可以手动地得到

该集合对象更改的通知信息。

细想下面的例子:

view plaincopy to clipboardprint?
01.<?xml version="1.0" encoding="utf-8"?>  
02.<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009
03.   xmlns:s="library://ns.adobe.com/flex/spark" 
04.   xmlns:mx="library://ns.adobe.com/flex/mx" 
05.   minWidth="1024" minHeight="768">  
06.                          
07.   <fx:Script>  
08.      <!--[CDATA[  
09.         import mx.events.FlexEvent;  
10.                                    
11.         [Bindable]  
12.         public var dp:Array = [ { label:"New York", data: "New York" },  
13.            { label:"Miami Beach", data: "Miami Beach" } ];  
14.                                    
15.      ]]-->  
16.   </fx:Script>  
17.                          
18.   <mx:ComboBox id="cb" editable="false" width="100" dataProvider="{dp}" />  
19.                          
20.</s:Application> 
<?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"
   xmlns:mx="library://ns.adobe.com/flex/mx"
   minWidth="1024" minHeight="768">
                       
   <fx:Script>
      <!--[CDATA[
         import mx.events.FlexEvent;
                                 
         [Bindable]
         public var dp:Array = [ { label:"New York", data: "New York" },
            { label:"Miami Beach", data: "Miami Beach" } ];
                                 
      ]]-->
   </fx:Script>
                       
   <mx:ComboBox id="cb" editable="false" width="100" dataProvider="{dp}" />
                       
</s:Application>

上面的代码看上去是flex应用程序的标准代码。但是,这时候的绑定不是必须的。你可以使用

事件的处理者直接给变量赋值,来代替绑定。

view plaincopy to clipboardprint?
01.<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009
02.   xmlns:s="library://ns.adobe.com/flex/spark" 
03.   xmlns:mx="library://ns.adobe.com/flex/mx" 
04.   minWidth="1024" minHeight="768" 
05.   creationComplete="creationCompleteHandler(event)">  
06.                          
07.   <fx:Script>  
08.      <!--[CDATA[  
09.         import mx.events.FlexEvent;  
10.                                    
11.         public var dp:Array = [ { label:"New York", data: "New York" },  
12.         { label:"Miami Beach", data: "Miami Beach" } ];  
13.                                    
14.         protected function creationCompleteHandler(event:FlexEvent):void 
15.         {  
16.            cb.dataProvider = dp;  
17.         }  
18.                                    
19.      ]]-->  
20.   </fx:Script>  
21.                          
22.   <mx:ComboBox id="cb" editable="false" width="100" />  
23.                          
24.</s:Application> 
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
   xmlns:s="library://ns.adobe.com/flex/spark"
   xmlns:mx="library://ns.adobe.com/flex/mx"
   minWidth="1024" minHeight="768"
   creationComplete="creationCompleteHandler(event)">
                       
   <fx:Script>
      <!--[CDATA[
         import mx.events.FlexEvent;
                                 
         public var dp:Array = [ { label:"New York", data: "New York" },
         { label:"Miami Beach", data: "Miami Beach" } ];
                                 
         protected function creationCompleteHandler(event:FlexEvent):void
         {
            cb.dataProvider = dp;
         }
                                 
      ]]-->
   </fx:Script>
                       
   <mx:ComboBox id="cb" editable="false" width="100" />
                       
</s:Application>

绑定一个类同时绑定该类的属性

另外一个普遍的错误是绑定一个类同时绑定该类的属性,比如:

view plaincopy to clipboardprint?
01.package  
02.{  
03.   [Bindable]  
04.   public class CustomerVO  
05.   {  
06.      [Bindable]  
07.      public var customerID:int;                               
08.      public function CustomerVO(customerID:int)  
09.      {  
10.         this.customerID = customerID;  
11.      }  
12.   }  
13.} 
package
{
   [Bindable]
   public class CustomerVO
   {
      [Bindable]
      public var customerID:int;                            
      public function CustomerVO(customerID:int)
      {
         this.customerID = customerID;
      }
   }
}


这时对customerID属性进行绑定是多余的,因为已对CustomerVO进行了绑定。这样做

不仅会在编译时带来警告,而且会生成多余的代码,影响程序的性能。像这样情况,应该

将多余的绑定删除掉。

在不支持的属性上使用双向绑定

 

 

Flex4 支持双向绑定。在创建绑定时在大括号前加@符号,而另外一个对象仍处于不被绑定

的状态。如下面的例子:

view plaincopy to clipboardprint?
01.<?xml version="1.0" encoding="utf-8"?>  
02.<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"                     
03.   xmlns:s="library://ns.adobe.com/flex/spark"   
04.   minWidth="1024" minHeight="768">  
05.   <s:TextInput text="@{someTextInput.text}"/>  
06.   <s:TextInput id="someTextInput" y="30"/>  
07.</s:Application> 
<?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"
   minWidth="1024" minHeight="768">
   <s:TextInput text="@{someTextInput.text}"/>
   <s:TextInput id="someTextInput" y="30"/>
</s:Application>

 

双向绑定在大多数情况下是工作的,但若绑定的属性是样式或效果的话,就不起作用了。

绑定到RemoteObject对象的arguments属性,HttpService、RemoteObject、WebService的

request属性也是不起作用的。看一下下面的例子:

view plaincopy to clipboardprint?
01.<?xml version="1.0" encoding="utf-8"?>      
02.<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"             
03.   xmlns:s="library://ns.adobe.com/flex/spark" 
04.   xmlns:mx="library://ns.adobe.com/flex/mx" 
05.   minWidth="1024" minHeight="768">  
06.   <fx:Script>  
07.      <!--[CDATA[  
08.         import mx.rpc.events.FaultEvent;            
09.         import mx.rpc.events.ResultEvent;  
10.   
11.         protected function resultHandler(event:ResultEvent):void 
12.         {  
13.            // handle result                                  
14.         }  
15.                                  
16.         protected function faultHandler(event:FaultEvent):void                                    
17.         {                                       
18.            // handle fault  
19.         }                              
20.      ]]-->                      
21.   </fx:Script>  
22.                         
23.   <fx:Declarations>  
24.      <s:HTTPService id="service" url="http://localhost/someservice.php"                                            
25.         result="resultHandler(event)"   
26.         fault="faultHandler(event)">  
27.         <mx:request xmlns="">  
28.            <username>@{someTextInput.text}</username>  
29.         </mx:request>                                   
30.                              
31.      </s:HTTPService>  
32.                         
33.   </fx:Declarations>  
34.                            
35.   <s:TextInput text="@{someTextInput.text}"/>  
36.                         
37.   <s:TextInput id="someTextInput" y="30"/>  
38.                         
39.   <s:Button click="service.send()" />  
40.   
41.</s:Application> 
<?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"
   xmlns:mx="library://ns.adobe.com/flex/mx"
   minWidth="1024" minHeight="768">
   <fx:Script>
      <!--[CDATA[
         import mx.rpc.events.FaultEvent;         
         import mx.rpc.events.ResultEvent;
 
         protected function resultHandler(event:ResultEvent):void
         {
            // handle result                               
         }
                               
         protected function faultHandler(event:FaultEvent):void                                 
         {                                    
            // handle fault
         }                           
      ]]-->                   
   </fx:Script>
                      
   <fx:Declarations>
      <s:HTTPService id="service" url="http://localhost/someservice.php"                                         
         result="resultHandler(event)"
         fault="faultHandler(event)">
         <mx:request xmlns="">
            <username>@{someTextInput.text}</username>
         </mx:request>                                
                           
      </s:HTTPService>
                      
   </fx:Declarations>
                         
   <s:TextInput text="@{someTextInput.text}"/>
                      
   <s:TextInput id="someTextInput" y="30"/>
                      
   <s:Button click="service.send()" />
 
</s:Application>


绑定不起作用的原因在于源和目标对象的属性必须支持绑定,而且能够支持读和写的操

作,只有这样才能支持双向绑定。


总之,数据绑定是Flex的很大的优点,但它也存在潜在的缺点。我建议在使用数据绑定

时,你要花时间考虑该绑定是不是必须的,使用得是不是完全正确。滥用或误用会带来

内存的消耗,影响程序的性能。

 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/lixuekun820/archive/2010/04/16/5492697.aspx

分享到:
评论

相关推荐

    flex数据绑定的原理

    6. **性能优化**:Flex数据绑定系统还包含了一些性能优化,比如变化传播的限制和缓存机制,以避免不必要的更新和提高效率。 7. **表达式绑定**:除了简单的变量绑定,Flex还支持复杂的表达式绑定,可以连接多个数据...

    FLEX资源——FLEX数据绑定专题(中文PDF)

    在Flex中,数据绑定是一种声明式编程方式,开发者无需编写复杂的事件处理代码,即可实现UI组件和数据模型之间的实时同步。这种机制极大地提高了开发效率,减少了出错的可能性。文档可能涵盖了以下几个主要知识点: ...

    FLEX 数据绑定专题一(转)

    数据绑定是连接应用程序UI(用户界面)组件和数据源的过程,当数据源发生变化时,UI会自动更新,反之亦然。这极大地简化了开发者的工作,因为他们无需手动编写代码来处理这些同步事件。在FLEX中,数据绑定是通过MXML...

    flex数据绑定 pdf

    这份名为“FLEX数据绑定专题”的PDF文档,很可能深入探讨了这一主题,提供了详细的理论知识和实践案例。 在Flex中,数据绑定是一个自动化的过程,它使得视图组件(如文本框、列表等)能够自动反映出数据源的变化,...

    FLEX数据绑定四种方式

    ### FLEX数据绑定四种方式详解 #### 一、概述 数据绑定是现代应用程序开发中一个重要的概念和技术。在Flex框架中,数据绑定被广泛应用来连接数据源与用户界面(UI)元素,使得数据能够实时更新并反映到界面上。本文将...

    flex数据绑定

    数据绑定是Flex和Adobe AIR应用程序开发中的关键概念之一,其主要目的是实现不同对象之间的数据传输与同步更新。通过数据绑定,开发者能够轻松地在用户界面元素(如文本框、列表等)与后端数据模型之间建立连接。...

    Flex数据绑定专题

    这种方式的优点在于可以更好地分离视图和数据逻辑,使得程序结构更加清晰。 3. **ActionScript中的`BindingUtils`方法**:这种方式允许在运行时动态地创建数据绑定,适用于需要根据程序运行状态动态调整绑定逻辑的...

    flex 数据绑定 dataGrid.rar

    Flex数据绑定是Adobe Flex开发中的核心特性之一,它允许开发者将UI组件的属性与应用程序的数据模型直接关联。在本示例中,我们关注的是如何在Flex中使用数据绑定技术来操作DataGrid组件,这是一个用于展示表格数据的...

    FLEX数据绑定专题

    在Flex 4中,数据绑定主要分为两种类型:声明式数据绑定和编程式数据绑定。声明式数据绑定通过XML语法在MXML文件中直接设置,如`&lt;mx:Label text="{data.name}" /&gt;`,这将Label组件的文本属性与名为"data"的对象的...

    flex企业应用开发笔记-数据绑定

    在Flex企业应用开发中,数据绑定...总之,Flex的数据绑定是构建动态和响应式用户界面的关键技术,它使得数据模型和用户界面之间的通信变得简单而高效。理解并熟练运用这一机制,能够提升Flex应用的开发效率和用户体验。

    Flex数据绑定[收集].pdf

    Flex数据绑定是软件开发中Adobe Flex或Adobe AIR应用程序的核心特性,它允许数据在不同对象之间自动同步。数据绑定简化了用户界面(UI)组件与数据源之间的交互,减少了手动更新的需要。然而,如果不理解其工作原理...

    flex页面跳转及数据绑定

    在Flex开发中,页面跳转和数据绑定是两个核心概念,它们对于构建动态且交互性强的应用至关重要。让我们分别深入探讨这两个主题。 首先,Flex页面跳转。在Flex中,有多种方式来实现场景间的切换: 1. **ViewStack...

    Flex Bind数据绑定实例代码

    Flex Bind数据绑定是Adobe Flex应用程序开发中的核心特性,它允许开发者在用户界面组件和应用程序模型之间建立动态连接,实现数据的自动同步。这种强大的功能极大地简化了编程逻辑,特别是处理用户交互和数据更新时...

    Flex 数据绑定

    Flex 数据绑定是Adobe Flex框架中的一个核心特性,它允许开发者创建动态、响应式的用户界面,使得数据模型的变化能够实时反映到视图上,反之亦然。这种机制大大简化了UI与后台数据同步的过程,减少了手动更新UI的...

    AMF抓取flex页面数据

    这需要理解Flex应用的逻辑和数据结构,通过发送AMF请求模拟用户交互。例如,使用编程语言(如Python、Java)编写脚本,构造AMF消息并发送到服务器,然后解析返回的数据。 6. **Flex数据解码** - 对于AMF数据的解码...

    flex3 数据绑定 总结

    签的 target 属性。下面的例子展示了如何使用 `&lt;mx:Binding&gt;` 实现相同的效果: ```xml ... &lt;mx:TextInput id="_...无论是简单的属性绑定,还是复杂的函数和对象绑定,Flex 3 都提供了丰富的工具和选项来满足各种需求。

    Flex初级数据绑定代码

    ### Flex初级数据绑定代码解析 #### 一、概述 本文将详细介绍一个Flex应用程序中的初级数据绑定实例。通过分析HelloWorld2.mxml文件,我们将深入了解Flex中的数据绑定机制及其基本用法。 #### 二、Flex与数据绑定...

    Flex高级数据绑定实例(界面语言切换)

    ### Flex高级数据绑定实例——界面语言切换 #### 概述 在Flex开发中,实现界面的多语言切换是一项常见的需求。本示例通过一个具体的代码片段来展示如何使用Flex中的高级数据绑定技术来实现这一功能。该示例不仅...

Global site tag (gtag.js) - Google Analytics