10.3 在List内部进行拖放
Drag and Drop Within a List
问题
你要在同一个列表类组件实例内部移动和复制数据。
解决方案
使用列表类组件中内置的拖放管理。
讨论
通过为DragManager调度的事件指定事件处理器,就可以启动任何组件的拖放功能。尽管你可以手动将这个功能添加到列表控件中,但Flex Framework中列表类组件,例如List、Tree和DataGrid,都内置了管理拖放操作的功能。列表类控件,即继承了mx.controls.listClasses.ListBase类的那些控件,不仅能够在内部处理来自DragManager的事件,还包含一些用来管理拖放操作和数据的私有方法和属性。
下面的例子显示了一个列表,通过设置dragEnabled和dropEnabled属性启用列表对拖放操作的支持,因此列表中的项目可在其数据集中移动:
- <mx:Application
- xmlns:mx="http://www.adobe.com/2006/mxml"
- layout="horizontal"
- creationComplete="creationHandler();">
- <mx:Script>
- <![CDATA[
- import mx.collections.ArrayCollection;
- private function creationHandler():void
- {
- var collection:ArrayCollection =
- new ArrayCollection( ['Josh', 'Todd', 'Abey'] );
- contactList.dataProvider = collection;
- }
- ]]>
- </mx:Script>
- <mx:Panel title="Contact List:"
- width="200" height="200">
- <mx:List id="contactList"
- width="100%" height="100%"
- dragEnabled="true"
- dropEnabled="true"
- dragMoveEnabled="true"
- />
- </mx:Panel>
- </mx:Application>
例子中这个List控件中的内联dragEnabled、dropEnabled和dragMoveEnabled属性每个都被定义为true。实质上dragEnabled和dropEnabled属性是使组件能够响应来自DragManager类事件的属性。dragMoveEnabled属性是用于指示列表中被拖拽的项目是移动到还是复制到列表中的标记。默认情况下,这个属性值为false,即支持将拖拽源数据复制添加到列表中。如果将dragMoveEnabled属性设置为true,则当放置事件完成时,列表中被拖拽的项目将会从原来的索引移动到鼠标指针所指向的目标索引。
如果通过设置或保持默认的dragMoveEnabled属性为false而启用数据复制模式的话,则在这个例子的List中执行拖放操作而选中某些选项时,你会看到一些奇怪的现象。这是因为采用数据集API类的基本操作都通过数据集里项目的唯一标识符(UID)来执行。
当一个项目被添加到列表类控件的dataProvider中时,protected ListBase.copyItemWithUID方法则被调用,为放置的项目赋予一个唯一标识符。但是,当数据源是一个含有String对象的简单数据类型列表时,例如本例中用于ArrayCollection的Array数据源,则在内部不会给这种项目赋予新的ID。因此,在这个例子的List控件中,当你执行任何类型的选择操作时,你会看到用这个UID总是选中最大索引处的那个项目。换句话说,如果你将列表中的第1个项目拖拽复制到第15索引处,那么无论何时你再次选择第1个项目,列表都会定位选择第15索引处的那个。
为了确保被复制的数据在添加到数据集中时是唯一的并被赋予UID,你可以为拖放操作添加事件处理器,用于复制应用程序所需的数据。下面的例子在前一个例子的基础上进行了修改,为List控件调度的dragComplete事件设置了事件处理器:
- <mx:Application
- xmlns:mx="http://www.adobe.com/2006/mxml"
- layout="horizontal"
- creationComplete="creationHandler();">
- <mx:Script>
- <![CDATA[
- import mx.utils.ObjectUtil;
- import mx.events.DragEvent;
- import mx.collections.ArrayCollection;
- private function creationHandler():void
- {
- var collection:ArrayCollection =
- new ArrayCollection( ['Josh', 'Todd', 'Abey'] );
- contactList.dataProvider = collection;
- }
- private function dropHandler( evt:DragEvent ):void
- {
- var listItem:Object = evt.dragSource.dataForFormat( "items" );
- var index:int = contactList.calculateDropIndex( evt );
- ArrayCollection( contactList.dataProvider ).setItemAt(
- ObjectUtil.copy( listItem ), index );
- }
- ]]>
- </mx:Script>
- <mx:Panel title="Contact List:"
- width="200" height="200">
- <mx:List id="contactList"
- width="100%" height="100%"
- dragEnabled="true"
- dropEnabled="true"
- dragMoveEnabled="false"
- dragComplete="dropHandler(event);"
- />
- </mx:Panel>
- </mx:Application>
将dragMoveEnabled属性设置为false启用复制操作,从而List实例能够在拖放行为结束时接受新的项目。内联的dragComplete事件属性将dropHandler方法注册为事件处理器,用于处理dragComplete事件。在这个事件处理器中,通过使用items的格式在DragSource中查找到当前正在被拖拽的项目,这种格式是在拖放操作启动时设置的内部格式。列表中的索引通过List.calculateDropIndex方法得到,用于更新添加到数据集中的项目。然后对项目进行深层复制,并调用ObjectUtil.copy方法为其指定新的唯一标识符。由于其中的dataProvider是一个ArrayCollection对象,因此这个数据集中任何元素的更新都会触发List实例中的数据绑定,任何改变都会立即反映到显示列表中。