增强型拖放组件:
List、DataGrid、PrintDataGrid、Tree、Menu、HorizontalList、TileList
这些组件只需要设置dragEnable="true"即可拖拽!
在不可直接拖拽的组件上实现拖放(重点)
1.把数据赋值给DragSource对象
2.检查格式是否允许把数据放入释放目标上
3.在释放目标里使用数据
4.允许拖拽组件
5.接收释放
DragSource类中的方法:
addData(data:*,format:String):void 向dragSource添加与格式向关联的数据
hasFormat(format:String):Boolean 检查DragSource对象包含于释放目标的format是否匹配
dataForFormat(format:String):Array of * 获取addData方法添加的能匹配format格式的数据
DragManager类中的方法:
doDrag(initiator:Component, 允许组件被拖放,通常用于mouseDown/mouseMove事件
dragSource:DragSource,
mouseEvent:MouseEvent):void
acceptDragDrop(target:Component):void 在DropEnter事件处理程序中调用该函数,通常用于hasFormat方法的if条件语句中
--------------------------------------------------------------------
拖放操作3步骤:
1.源的初始化
2.拖拽
3.释放
每步都会触发一个事件,通过事件监听实现数据的传递!
也就是,点击源的时候,获取到数据,当进入到释放目的地的时候,触发一个函数执行,从event中获取数据,将数据填充到目的地中!
要实现拖与放,必须设置两个最基本的属性:
被拖拽的组件设置:dragEnable="true"
目的组件设置:dropEnable="true"
1.相同组件之间的拖放,将默认使用同名属性进行数据填充。
两个DataGrid之间的拖放
<?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"
creationComplete="prodData.send()"> <!-- 组件构建完成后,调用send()向远端请求数据-->
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.rpc.events.ResultEvent;
[Bindable]
private var products:ArrayCollection;//存储数据的集合
private function resultHandler(event:ResultEvent):void{
products=event.result.catalog.product;//从返回的数据中检索product子节点,并放入集合中
}
]]>
</fx:Script>
<s:layout>
<s:VerticalLayout gap="50"
paddingLeft="20" paddingTop="20"/>
</s:layout>
<fx:Declarations>
<!-- HTTPService从服务器获取数据 -->
<s:HTTPService id="prodData"
url="http://www.flexgrocer.com/product.xml"
result="resultHandler(event)"/>
</fx:Declarations>
<mx:DataGrid id="initiatorGrid"
dataProvider="{products}"
dragEnabled="true"> <!--拖拽源设置dragEnabled="true"-->
<mx:columns>
<mx:DataGridColumn dataField="name" headerText="Product"/>
<mx:DataGridColumn dataField="cost" headerText="Price"/>
<mx:DataGridColumn dataField="isLowFat" headerText="Low Fat"/>
<mx:DataGridColumn dataField="isOrganic" headerText="Organic"/>
</mx:columns>
</mx:DataGrid>
<mx:DataGrid id="targetGrid" dropEnabled="true"> <!-- 释放目标,设置dropEnabled="true"-->
<mx:columns>
<mx:DataGridColumn dataField="name" headerText="Product"/>
<mx:DataGridColumn dataField="category" headerText="Category"/>
</mx:columns>
</mx:DataGrid>
</s:Application>
2.不同组件之间的拖放,不能使用"默认释放行为",因为组件不同,程序不知道该将如何处理数据,只好放一个Object对象到目标组件上!这不是我们想要的结果!
在目标组件上定义dragDrop事件,并生成一个监听DragEvent事件的方法。
在方法内部:
var dgRow:Object = event.dragSource.dataForFormat("items");
某行的数据将被全部放到dgRow中存放起来。
注:如果要实现一次拖拽多行数据,只需设置DataGrid的allowMultipleSelection="true"
3.定义一个ArrayCollection用来存放将被放置进入的对象的某些属性
[Bindable]
private var targetListDP:ArrayCollection=new ArrayCollection();
然后目标DataGrid中设置dataProvider="{targetListDP}"
这样做的目的是:在事件监听处理函数中,向这个collection中放入我们自己指定的数据!在方法中:通过DataGrip定义的id(可看做一个对象)调用到其dataProvider属性,然后
addItem(dgRow[0].name),这里只是将dgRow中的name属性的值取出,同样可以取其它值,只需要改变属性名称即可!
4.最后,某些时候虽然自定义了拖放时数据的放置规则,但程序仍然会将默认的数据放置到目的地,这时,可以在事件处理方法中:event.preventDefault();
将数据从DataGrid拖放到List组件
<?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"
creationComplete="prodData.send()">
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.events.DragEvent;
import mx.rpc.events.ResultEvent;
[Bindable]
private var products:ArrayCollection;//存放HTTPService从服务器请求的数据
[Bindable]
private var targetListDP:ArrayCollection=new ArrayCollection();
//在拖拽事件完成时,将事件对象带过来的数据添加到该集合中,该集合作为目标的dataProvider
private function resultHandler(event:ResultEvent):void{
products=event.result.catalog.product;
}
protected function targetList_dragDropHandler(event:DragEvent):void
{
// 拖拽结束时,从事件对象中获取数据,设置到目标的dataProvider中
//记住:与DataGrid中数据相关的格式总是"items"
var dgRow:Object = event.dragSource.dataForFormat("items");
//将数据设置到目标所绑定的dataProvider中
targetListDP.addItem(dgRow[0].name);
//阻止事件的默认行为(将数据以Object对象的形式放到目标中)
event.preventDefault();
}
]]>
</fx:Script>
<s:layout>
<s:VerticalLayout gap="30"
paddingLeft="20" paddingTop="20"/>
</s:layout>
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
<s:HTTPService id="prodData"
url="http://www.flexgrocer.com/product.xml"
result="resultHandler(event)"/>
</fx:Declarations>
<mx:DataGrid id="initiatorGrid"
dataProvider="{products}"
dragEnabled="true">
<mx:columns>
<mx:DataGridColumn dataField="name" headerText="Product"/>
<mx:DataGridColumn dataField="cost" headerText="Prine"/>
<mx:DataGridColumn dataField="isLowFat" headerText="Low Fat"/>
<mx:DataGridColumn dataField="isOrganic" headerText="Organic"/>
</mx:columns>
</mx:DataGrid>
<s:List id="targetList"
width="200"
dropEnabled="true"
dataProvider="{targetListDP}"
dragDrop="targetList_dragDropHandler(event)"/>
</s:Application>
--------------------------------------------------------------------
非增强型组件(程序中默认不具备拖拽功能的组件)的拖放
5步:
1.在拖拽源上定义mouseDown事件
2.在事件处理函数至少定义4个形式参数准备接收数据:
initiator:Label,//描述源
dsData:String,//描述数据
event:MouseEvent,//描述事件
format:String//描述格式
3.新建一个DragSource对象:var ds:DragSource = new DragSource();
将数据和格式放入到对象中:ds.addData(dsData,format);
开启拖拽功能:DragManager.doDrag(initiator as IUIComponent,ds,event);
4.到此,源已经可以拖动了,数据也被放置到了DataSource对象中保存起来了
下面就需要对目标进行操作了
目标上设置dragEnter事件,事件处理函数中判断格式是否匹配
如果匹配,则接收释放,否则不做任何处理
if(event.dragSource.dataForFormat(format))
DragManager.acceptDragDrop(event.target as IUIComponent);
5.到此,目标可以接收拖放,但是目标并为处理数据
处理DataSource对象中存放的数据
目标上定义dragDrop事件
事件处理函数中,根据指定的format从DragSource中获取数据,
设置到目标的dataProvider中,实现将数据填充到目标上
var myLabelData:Object = event.dragSource.dataForFormat(format);
myList.dataProvider.addItem(myLabelData);
6.到此,实现非增强型组件的拖拽和目标正确显示数据
--------------------------------------------------------------------------------
源码
说明:将Label拖拽到List中,并在List中呈现数据
<?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">
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.core.DragSource;
import mx.core.IUIComponent;
import mx.events.DragEvent;
import mx.managers.DragManager;
[Bindable]
private var myListDP:ArrayCollection=new ArrayCollection();//dataProvider属性对应的数据存放仓库(集合)
//1.被拖拽组件发生mouseDown事件
protected function myLabel_mouseDownHandler(initiator:Label,dsData:String,event:MouseEvent,format:String):void
{
var ds:DragSource = new DragSource();//创建DragSource对象
ds.addData(dsData,format);//将数据以及指定的格式设置到DragSource中
DragManager.doDrag(initiator as IUIComponent,ds,event);//开启拖拽功能
}
//2.被拖拽的组件(代理)进入到目的地时,检查格式是否匹配,如果匹配,则接受释放
protected function myList_dragEnterHandler(event:DragEvent,format:String):void
{
if(event.dragSource.dataForFormat(format))//如果DragSource中的format格式与形参中的format匹配,则接收释放
DragManager.acceptDragDrop(event.target as IUIComponent);
}
//3.释放事件发生后,处理数据
protected function myList_dragDropHandler(event:DragEvent,format:String):void
{
var myLabelData:Object = event.dragSource.dataForFormat(format);//从DragSource中获取数据
myList.dataProvider.addItem(myLabelData);//设置到List的dataProvider中,数据得以呈现
}
]]>
</fx:Script>
<s:layout>
<s:VerticalLayout gap="20"
paddingLeft="20" paddingTop="20"/>
</s:layout>
<!--被拖拽的Label -->
<s:Label id="myLabel"
text="Drag me"
mouseDown="myLabel_mouseDownHandler(myLabel,'something',event,'this format')"/>
<!--目的地-->
<s:List id="myList"
width="200"
dataProvider="{myListDP}"
dragEnter="myList_dragEnterHandler(event,'this format')"
dragDrop="myList_dragDropHandler(event,'this format')"/>
</s:Application>
--------------------------------------------------------------------
实现图片的拖拽
1.图片上定义mouseDown事件,将数据放入DragSource对象中
ProductItem.mxml
<?xml version="1.0" encoding="utf-8"?>
<s:DataRenderer xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
width="100%">
<s:states>
<s:State name="State1"/>
<s:State name="expanded"/>
</s:states>
<!-- 声明自定义事件 -->
<fx:Metadata>
[Event(name="addProduct", type="events.ProductEvent")]
[Event(name="removeProduct", type="events.ProductEvent")]
</fx:Metadata>
<fx:Script>
<![CDATA[
import cart.ShoppingCart;
import cart.ShoppingCartItem;
import events.ProductEvent;
import mx.core.DragSource;
import mx.core.IUIComponent;
import mx.managers.DragManager;
import spark.components.Group;
import spark.primitives.BitmapImage;
import valueObjects.Product;
[Bindable]
public var product:Product;//由flex在为data赋值的时候,将value赋值给product
//public var shoppingCart:ShoppingCart;//购物车从哪里传入呢?通过事件派发添加/删除事件,在ShoppingView中完成,而不是在ProductItem中完成!
//添加某类商品到购物车,添加商品会派发一个addProduct事件
private function addToCart(product:Product):void {
var event:Event = new ProductEvent("addProduct",product);//将product放到事件对象event中
this.dispatchEvent(event);//派发事件
}
//从购物车中删除该类商品,派发一个removeProduct事件
private function removeFromCart( product:Product ):void {
var event:Event = new ProductEvent("removeProduct",product);//将product放到事件对象event中
this.dispatchEvent(event);//派发事件
}
//当指定了dataProvider之后,flex会针对数据集中每个数据项创建一个呈现器实例
//然后将取得的数据放到data中保存
//这里覆盖data的set方法,利用上述原理,在赋值时直接将数据赋值到product对象中,而不是赋值给data
public override function set data(value:Object) :void {
this.product = value as Product; //注意进行类型转换
}
// 图片发生mouseDown事件
protected function img_mouseDownHandler(event:MouseEvent,format:String):void
{
//创建一个图片代理
var proxy:BitmapImage = new BitmapImage();
proxy.source = img.content;//获取图片数据放入BItmapImage对象中
//因为proxy不是IFlexDisplayObject类型,不能直接作为参数加入到doDrag方法中
//这里通过Group来进行包装
var imageHolder:Group = new Group();
imageHolder.addElement(proxy);
//将product对象放入DragSource对象中
var dragSource:DragSource = new DragSource();
dragSource.addData(product,format);
//开启image组件的拖放功能,必须指定3个参数:初始器、DragSource对象、事件
DragManager.doDrag(event.currentTarget as IUIComponent,dragSource,event,imageHolder);
}
]]>
</fx:Script>
<fx:Declarations>
<!-- 将非可视元素(例如服务、值对象)放在此处 -->
</fx:Declarations>
<!-- 商品 -->
<s:VGroup id="products">
<s:Label text="{product.prodName}" id="prodName"/>
<mx:Image source="assets/{product.imageName}" scaleContent="true"
mouseOver="this.currentState='expanded'"
mouseOut="this.currentState='State1'"
id="img"
mouseDown="img_mouseDownHandler(event,'cartFormat')"/>
<s:Label text="${product.listPrice}" id="price"/>
<s:Button label="AddToCart" id="add"
click="addToCart(product )"/>
<s:Button label="Remove From Cart" id="remove"
click="removeFromCart(product )"/>
</s:VGroup>
<!-- 商品详细信息 -->
<s:VGroup includeIn="expanded" x="200" width="100%">
<s:RichText text="{product.description}"
width="50%"/>
<s:Label text="Certified Organic"
visible="{product.isOrganic}"/>
<s:Label text="Low Fat"
visible="{product.isLowFat}"/>
</s:VGroup>
</s:DataRenderer>
2.在目标上定义DragEnter事件,根据format判断是否允许进入
3.在目标上定义DragDrop事件,通过format从DragSource对象中获取数据
ShoppingView.mxml
<?xml version="1.0" encoding="utf-8"?>
<s:Group xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" width="0" height="0" xmlns:components="components.*">
<s:layout>
<s:HorizontalLayout/>
</s:layout>
<s:states>
<s:State name="State1"/>
<s:State name="cartView"/>
</s:states>
<fx:Script>
<![CDATA[
import cart.ShoppingCart;
import cart.ShoppingCartItem;
import components.ProductItem;
import events.ProductEvent;
import mx.collections.ArrayCollection;
import mx.core.IUIComponent;
import mx.events.DragEvent;
import mx.managers.DragManager;
import valueObjects.Product;
[Bindable]
public var shoppingCart:ShoppingCart = new ShoppingCart();//创建一个购物车,每种商品都使用同一个购物车!
[Bindable]
public var groceryInventory:ArrayCollection;//用于存放HTTPService返回的各种商品信息
//查看当前购物车中的商品
private function handleViewCartClick( event:MouseEvent ):void {
this.currentState="cartView";
}
//该方法返回一个字符串,用来作为数据集的呈现方式,由labelFunction函数指定
//当List中使用labelFunction时,会自动以正确的方式从dataProvider中获取数据并传递到该方法中
//返回的字符串将作为被呈现的内容
private function renderProductName(item:ShoppingCartItem):String {
var product:Product = item.product;
return "("+item.quantity+")" + product.prodName + " $" + item.subtotal;
}
//对ProductItem派发的addProduct事件进行监听并处理
public function addProductHandler(event:ProductEvent):void {
var sci:ShoppingCartItem = new ShoppingCartItem(event.product);//从自定义事件中获取属性
shoppingCart.addItem(sci);
}
//对ProductItem派发的removeProduct事件进行监听并处理
public function removeProductHandler(event:ProductEvent):void {
var sci:ShoppingCartItem = new ShoppingCartItem(event.product);//从自定义事件中获取属性
shoppingCart.removeItem(sci);
}
//拖拽图片进入到购物车List组件
protected function cartList_dragEnterHandler(event:DragEvent,format:String):void
{
// 检测格式,匹配则接受释放
if(event.dragSource.hasFormat(format))
DragManager.acceptDragDrop(event.target as IUIComponent);
}
//释放图片事件发生
protected function cartList_dragDropHandler(event:DragEvent,format:String):void
{
// 接受释放之后,从DragSource对象中获取数据
var product:Product = event.dragSource.dataForFormat(format) as Product;
// 将product加入到shoppingCartItem中
var shoppingCartItem:ShoppingCartItem = new ShoppingCartItem(product,1);
// 将shoppingCartItem加入到购物车对象中(该对象为购物车List的dataProvider)
shoppingCart.addItem(shoppingCartItem);
}
]]>
</fx:Script>
<fx:Declarations>
<!-- 将非可视元素(例如服务、值对象)放在此处 -->
</fx:Declarations>
<!-- 该DataGroup被下面的ProductList取代
通过事件冒泡实现商品的添加与删除
因为原来在ProductItem中直接对购物车添加或删除商品的操作无法进行
原因是无法对每个ProductItem都传入同一个购物车
[虽然this.parent.parent.shoppingCart可以访问到购物车,但是依赖性太强,不推荐]
<s:DataGroup width="100%" height="100%"
width.cartView="0" height.cartView="0" visible.cartView="false"
dataProvider="{groceryInventory}"
itemRenderer="components.ProductItem">
<s:layout>
<s:VerticalLayout/>
</s:layout>
</s:DataGroup>
-->
<!-- 使用具有事件监听出派发功能的组件替代原来的DataGroup-->
<!-- 实现添加/删除商品的步骤:
ProductList作为一个组件,通过dataProvider获取到数据
在ProductList中又指名了itemRenderer为ProductItem
这样,ProductItem中的product属性就可以被赋予值
当添加商品的时候,会触发addProduct事件,而且事件可以冒泡
在ProductList中对该事件进行了声明,则可以对该事件继续向上冒泡
这样,在ShoppingView中就可以对这个事件进行捕获并处理!!!
然后,在ProductList组件中就可以指定事件发生后的处理函数了!!!
-->
<components:ProductList width="100%" height="100%"
width.cartView="0" height.cartView="0" visible.cartView="false"
dataProvider="{groceryInventory}"
addProduct="addProductHandler(event)"
removeProduct="removeProductHandler(event)"/>
<!-- 购物车组件 -->
<s:VGroup id="cartGroup" height="100%" width.cartView="100%">
<s:List id="cartList"
dataProvider="{shoppingCart.items}" includeIn="State1"
labelFunction="renderProductName"
dragEnter="cartList_dragEnterHandler(event,'cartFormat')"
dragDrop="cartList_dragDropHandler(event,'cartFormat')"/>
<s:Label text="Your Cart Total: ${shoppingCart.total}"/>
<s:Button label="View Cart" click="handleViewCartClick( event )" includeIn="State1"/>
<!-- 使用自定义的DataGrid呈现购物车的详细信息-->
<components:CartGrid id="dgCart" includeIn="cartView" width="100%" height="100%"
dataProvider="{shoppingCart.items}"
removeProduct="removeProductHandler(event)"/>
<s:Button includeIn="cartView" label="Continue Shopping" click="this.currentState=''"/>
</s:VGroup>
</s:Group>