Collection事件和手动变化通知
集合用事件来表示集合发后了变化. 你能用这些事件监控变化以作相应的显示上的更新.
Collection事件: CollectionEvent, PropertyChangeEvent和FlexEvent.
- 当今集合发生变化时, 发布 CollectionEvevnt.COLLECTION_CHANGE.
- CollectionEvent.kind(CollectionEventKind类型)用来表示是什么变化类型,比如UPDATE.
- CollectionEvent.items(是一个Array类型), 如果是ADD和REMOVE kind 事件, 这个数组包含了被删除或是被增加的items. 对于UPDATE事件, items则是一个事件数组,数组成员全都是PropertyChangeEvent, 每个事件表示相应的item的update
- PropertyChangeEvent.kind(类型是PropertyChangeEventKind)表示是那个属性发生了变化.
- 当游标位置发生了变化, 视图游标(View cursor)发布一个事件. type属性是FlexEvent.CURSOR_UPDATE
你可以用这些集合事件来监控集合变化以更新显示。例如,一个自定义控件用一个collection作为它的data provider, 你肯定想每次collection发生变化时控件能被自动更新以显示修改后的最新数据. 这时上面这些集合事件就能派上用场.
假设我们做一个汽车租赁的预定系统. 应用程序就能用COLLECTION_CHANGE 事件来监听 预定信息和汽车信息.为这个事件的侦听器函数起个名字叫 reservationsChanged,这个函数判断 Change事件的kind 字段以作以下的不同业务逻辑.
- 如果kind是ADD, 遍历事件的items属性,调用一个函数更新预定信息的显示(一个显示所有预定时间的框)
- 如果kind是REMOVE, 遍历事件的items属性, 调用一个函数将这些预定信息从框中删除
- 如果kind是UPDATE, 遍历事件的items属性, 此时每个item是一个PropertyChangeEvent事件对象, 我们调用函数更新所有对应的预定信息.
- 如果kind是RESET, 调用一个函数重置预定信息.
代码如下:
private function reservationsChanged (event :CollectionEvent ) : void { switch (event.kind ) { case CollectionEventKind. ADD : updateReservationBox (Reservation (event.items [i ] ) ); } break; case CollectionEventKind.REMOVE : removeReservationBox (Reservation (event.items [i ] ) ); } break; case CollectionEventKind.UPDATE : if (event.items [i ] is PropertyChangeEvent ) { if (PropertyChangeEvent (event.items [i ] ) != null ) { updateReservationBox (Reservation (PropertyChangeEvent ( event.items [i ] ). source ) ); } } else if (event.items [i ] is Reservation ) { updateReservationBox (Reservation (event.items [i ] ) ); } } break; case CollectionEventKind.RESET : refreshReservations ( ); break; } }
大家知道, 没有实现IEventDispatcher接口的数据项是不可监控的, 而且Flash和Flex对象和其它的基本数据类型都没有实现这个接口. 因此当你修改了Array或是一个DisplayObject对象的属性时,你就必须调用itemUpdated()方法来手动更新集合视图, 这个方法将 已被修改的item对象 和 修改之前的item对象 一并作为参数.
当你必须用Array来直接作为控件的dataProvider时, 你也可以用itemUpdated()方法来手动通知collection发生了变化。Array直接作dataProvider时, 控件会将数据封装到一个collection对象, 但是这个collection封装对象在Array数据发生改变时是不知道的,所以必须用itemUpdated()手动通知。
注: 当你直接在一个collection中增加或删除子项时,或是通过ICollectionView, IList的方法来修改colletion时, 你完全没有必须调用itemUpdated().
大家知道, 当一个类,或是一个对象的声明上加[Bindable]时,需要类(或是对象的类)实现了IEventDispatcher接口。因为只有实现了IEventDispatcher接口才能发布事件(propertyChange事件).
当你在类的声明之上加[Bindable]时,这个类的任何属性在发生变化时,都会发布propertyChange事件; 如果你仅是在指定属性之上加[Bindable]标记时,那只有加了[Bindable] meta tag的这些属性才会发布propertyChange事件。
collection会侦听propertyChange事件. 假设你有一个 myCollection, 这个collection的item的类有一个[Bindable] myVariable的变量,那么 myCollection.getItemAt(0).myVariable="myText" 将会触发一个propertyChange事件(假定是没有必要使用itemUpdated()的情况).
所以最常见的itemUpdate用法是: 一个不能Bindable的类,或是无法实现IEventDispatcher接口的类的数据(属性)发生变化时来用通知collection.下面的例子展示在这样的情景时你就可以用itemUpdated()
假设你有一个你不能控制和再编辑的类:
public class ClassICantEdit { public var field1:String; public var field2:String;}
你还有一个ArrayCollection, 里面的item全都是 classICantEdit对象.
public var myCollection:ArrayCollection = new ArrayCollection();
你有如下一个DataGrid控件:
<mx:DataGrid dataProvider="{myCollection}"/>
当你象下面更改myCollection中的item的值时, DataGrid控件是不会自动更新的:
myCollection.getItemAt(0).field1="someOtherValue";
为更新DataGrid控件,你必须 itemUpdated()函数:
myCollection.itemUpdated(collectionOfThoseClasses.getItemAt(0));
禁止和启用自动更新
Collection还提供了enableAutoUpdate()和disableAutoUpdate()方法,这两个方法可以启用或是禁止数据发生变化时自动更新数据视图的功能。
collection的 disableAutoUpdate()方法会阻止基本数据改变事件被collection视图广播.同时还阻止,collection自身作为一个结果集被改变的事件.
当一个collection绑定为一个控件的dataProvider时, 用这个方法可以防止因collection多次变化而引起控件不必要的中间显示更新。
例如, DataGrid控件在item被选中时,就会调用disableAutoUpdate(), 当这个item不再被选中时再调用enableAutoUpdate(),这样可以当你正在编辑一个item时,不会因为这item是在一个排序的collection中而导致在屏幕上下乱跳.
下面的代码片断显示了怎样调用disableAutoUpdate(), enableAutoUpdate()
var obj:myObject = myCollection.getItemAt(0);
myCollection.disableAutoUpdate();
obj.prop1 = 'foo';
obj.prop2 = 'bar';
myCollection.enableAutoUpdate();
例子: 在DataGrid控件中修改数据
下面的例子你可以在DataGrid控件中增加, 删除, 修改数据
<?xml version= "1.0" ?> <!-- dpcontrols/ModifyDataGridData.mxml --> <mx :Application xmlns :mx= "http://www.adobe.com/2006/mxml" width= "500" height= "600" > <mx :Script > <! [CDATA [ import mx.events. *; import mx.collections. *; // Add event information to a log (displayed in the TextArea). public function collectionEventHandler (event :CollectionEvent ) : void { switch (event.kind ) { case CollectionEventKind. ADD : addLog ( "Item " + event.location + " added" ); break; case CollectionEventKind.REMOVE : addLog ( "Item " + event.location + " removed" ); break; case CollectionEventKind.REPLACE : addLog ( "Item " + event.location + " Replaced" ); break; case CollectionEventKind.UPDATE : addLog ( "Item updated" ); break; } } // Helper function for adding information to the log. log. text += str + "/n"; } // Add a person to the ArrayCollection. public function addPerson ( ) : void { ac.addItem ( {first :firstInput. text, last :lastInput. text, email :emailInput. text } ); clearInputs ( ); } // Remove a person from the ArrayCollection. public function removePerson ( ) : void { // Make sure an item is selected. if (dg.selectedIndex >= 0 ) { ac.removeItemAt (dg.selectedIndex ); } } // Update an existing person in the ArrayCollection. public function updatePerson ( ) : void { // Make sure an item is selected. if (dg.selectedItem !== null ) { ac.setItemAt ( {first :firstInput. text, last :lastInput. text, email :emailInput. text }, dg.selectedIndex ); } } // The change event listener for the DataGrid. // Clears the text input controls and updates them with the contents // of the selected item. public function dgChangeHandler ( ) : void { clearInputs ( ); firstInput. text = dg.selectedItem.first; lastInput. text = dg.selectedItem.last; emailInput. text = dg.selectedItem.email; } // Clear the text from the input controls. public function clearInputs ( ) : void { firstInput. text = ""; lastInput. text = ""; emailInput. text = ""; } // The labelFunction for the ComboBox; // Puts first and last names in the ComboBox. return item.first + " " + item.last; } ] ] > </mx :Script > <!-- The ArrayCollection used by the DataGrid and ComboBox. --> <mx :ArrayCollection id= "ac" collectionChange= "collectionEventHandler(event)" > <mx :source > </mx :source > </mx :ArrayCollection > <mx :DataGrid width= "450" id= "dg" dataProvider= "{ac}" change= "dgChangeHandler()" > <mx :columns > <mx :DataGridColumn dataField= "first" headerText= "First Name" /> <mx :DataGridColumn dataField= "last" headerText= "Last Name" /> <mx :DataGridColumn dataField= "email" headerText= "Email" /> </mx :columns > </mx :DataGrid > <!-- The ComboBox and DataGrid controls share an ArrayCollection as their data provider. The ComboBox control uses the labelFunction property to construct the labels from the dataProvider fields. --> <mx :ComboBox id= "cb" dataProvider= "{ac}" labelFunction= "myLabelFunc" /> <!-- Form for data to add or change in the ArrayCollection. --> <mx :Form > <mx :FormItem label= "First Name" > <mx :TextInput id= "firstInput" /> </mx :FormItem > <mx :FormItem label= "Last Name" > <mx :TextInput id= "lastInput" /> </mx :FormItem > <mx :FormItem label= "Email" > <mx :TextInput id= "emailInput" /> </mx :FormItem > </mx :Form > <mx :HBox > <!-- Buttons to initiate operations on the collection. --> <mx :Button label= "Add New" click= "addPerson()" /> <mx :Button label= "Update Selected" click= "updatePerson()" /> <mx :Button label= "Remove Selected" click= "removePerson()" /> <!-- Clear the text input fields. --> <mx :Button label= "Clear" click= "clearInputs()" /> </mx :HBox > <!-- The application displays event information here --> <mx :Label text= "Log" /> <mx :TextArea id= "log" width= "100" height= "100%" /> </mx :Application >
Flash显示效果请到 http://livedocs.adobe.com/flex/3/html/help.html?content=about_dataproviders_5.html 文章最后部分查看.