(十三)Flex4_实现拖放功能

增强型拖放组件:
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>






你可能感兴趣的:(flex4)