拖拽事件监听器和回调方法
View对象既可以用实现View.OnDragListener接口的拖放事件监听器,也可以用View对象的onDragEvent(DragEvent)回调方法来接收拖拽事件。当系统调用这个回调方法或监听器时,都要给它们传递一个DragEvent对象。
在大多数场景中你可能会使用监听器。因为在设计UI界面时,通常没有View类的子类,而使用回调方法,为了覆写这个方法,就会强制你使用View类及子类。相比之下,你可以实现一个监听器类,然后再几个不同的View对象中使用。你还可以把监听器接口作为一个匿名的内部类来实现。调用setOnDragListener()方法给View对象设置监听器。
View对象能够同时拥有监听器和回调方法,如果发生这种情况,系统会首先调用监听器。除非监听器返回了false,否则系统不会调用回调方法。
onDragEvent(DragEvent)回调方法和View.OnDragListener监听器的组合跟用于触屏事件的onTouchEvent()回调方法和View.OnTouchListener监听器类似。
拖拽事件
系统用DragEvent对象形式的拖拽事件。这个对象包含了一个操作类型,它告诉监听器在拖放过程中发生的事情。这个对象还根据操作类型,包含了其他的数据。
调用getAction()方法能够获得操作类型。有六种可能的值,在DragEvent类中被定义成常量。详细见下表1.
DragEvent对象还包含了应用程序在调用startDrag()方法时,提供给系统的数据。这些数据中有些只对特定的操作类型有效。在下表2中概要的介绍了对每种操作类型可以获取的有效数据。
表1.DragEvent操作类型
getAction()方法返回值 |
含义 |
ACTION_DRAG_STARTED |
只在应用程序调用startDrag()方法,并且获得了拖拽影子后,View对象的拖拽事件监听器才接收这种事件操作。 |
ACTION_DRAG_ENTERED |
当拖拽影子刚进入View对象的边框时,View对象的拖拽事件监听器会接收这种事件操作类型。 |
ACTION_DRAG_LOCATION |
在View对象收到一个ACTION_DRAG_ENTERED事件之后,并且拖拽影子依然还在这个对象的边框之内时,这个View对象的拖拽事件监听器会接收这种事件操作类型 |
ACTION_DRAG_EXITED |
View对象收到一个ACTION_DRAG_ENTERED和至少一个ACTION_DRAG_LOCATION事件之后,这个对象的事件监听器会接受这种操作类型。 |
ACTION_DROP |
当用户在一个View对象之上释放了拖拽影子,这个对象的拖拽事件监听器就会收到这种操作类型。如果这个监听器在响应ACTION_DRAG_STARTED拖拽事件中返回了true,那么这种操作类型只会发送给一个View对象。如果用户在没有被注册监听器的View对象上释放了拖拽影子,或者用户没有在当前布局的任何部分释放操作影子,这个操作类型就不会被发送。如果View对象成功的处理放下事件,监听器要返回true,否则应该返回false。 |
ACTION_DRAG_ENDED |
当系统结束拖拽操作时,View对象拖拽监听器会接收这种事件操作类型。这种操作类型之前不一定是ACTION_DROP事件。如果系统发送了一个ACTION_DROP事件,那么接收ACTION_DRAG_ENDED操作类型不意味着放下操作成功了。监听器必须调用getResult()方法来获得响应ACTION_DROP事件中的返回值。如果ACTION_DROP事件没有被发送,那么getResult()会返回false。 |
表2.通过操作类型事件获取有效的DragEvent对象的数据,x代表能够获取有效数据。
getAction() Value |
getClipDescription() |
getLocalState() |
getX() |
getY() |
getClipData() |
getResult() |
ACTION_DRAG_STARTED |
X |
X |
X |
X |
|
|
ACTION_DRAG_ENTERED |
X |
X |
|
|
|
|
ACTION_DRAG_LOCATION |
X |
X |
X |
X |
|
|
ACTION_DRAG_EXITED |
X |
X |
|
|
|
|
ACTION_DROP |
X |
X |
X |
X |
X |
|
ACTION_DRAG_ENDED |
X |
X |
|
|
|
X |
getAction()、describeContents()、writeToParcel()和toString方法始终返回有效的数据。
对于特殊的操作类型,如果一个方法不包含有效的数据,就会根据类型的不同而返回null或0。
拖拽影子
在拖拽和放下操作期间,系统会显示一张用户拖动的图片。对于要移动的数据,这张图片就代表了被拖动的数据。对于操作,这张图片就代表了拖动操作的某些外观。
这张图片被叫做拖动影子,使用View.DragShadowBuilder对象的方法来创建它,并且在使用startDrag()方法开始拖拽时,把这个对象传递给系统。作为响应startDrag()方法的一部分,系统会调用在View.DragShadowBuilder对象中定义的回调方法,来获取拖拽影子。
View.DragShadowBuilder类有两个构造器:
View.DragShadowBuilder(View):
这个构造器接收任意的应用程序的View对象。这个构造器把View对象保存在View.DragShadowBuilder对象中,以便在回调期间访问这个View对象,来构造拖拽影子。它(View对象参数)不必跟用户选择的开始拖拽操作的View对象相关联。
如果使用这个构造器,就不必扩展View.DragShadowBuilder类或覆写它的方法。默认情况,你会获得一个跟传递给构造器的View对象外观相同的拖拽影子。在用户的触屏位置下方,以出点为中心显示。
View.DragShadowBuilder():
如果使用这个构造器,在ViewDragShadowBuilder对象中没有有效的View对象。默认情况下,如果使用这个构造器,并且没有扩展View.DragShadowBuilder类或覆写它的方法,那么就会获得一个不可见的拖拽影子,系统不会给出错误。
ViewDragShadowBuilder类有两个方法:
onProvideShadowMetrics():
在你调用startDrag()方法后,系统会立即调用这个方法,给系统发送拖拽影子的尺寸和触点。这个方法有两个参数:
dimensions:一个Point对象,其中X代表影子的宽度,Y代表影子的高度;
touch_point:一个Point对象,这个触点应该是拖拽期间用户手指下方的拖拽影子的位置,X代表x轴的坐标,Y代表y轴的坐标。
onDrawShadow():
调用onProviderShadowMetrics()回调之后,系统会立即调用onDrawShadow()方法来获得拖拽影子。这个方法有一个画布参数(Canvas对象),系统会使用onProvideShadowMetrics()方法中提供的参数来构造这个Canvas对象,并在这个对象中描画拖拽影子。
要改善性能,就要保持拖拽影子要用小的尺寸。对于一个单独的项目,可以使用一个图标,对于多项选择,可以是堆栈中的图标而不是分散在屏幕上的完整的图片。