原文自:http://android.eoe.cn/topic/ui
使用Android的拖放框架,允许用户通过一个图形化的拖放动作,把数据从当前布局中的一个视图上转移到另一个视图上。这个框架包含了一个拖动事件类,拖动监听器和一些辅助的方法和类。
虽然这个框架主要是为了数据的移动而设计的,但是你可以将这些移动的数据提供给其他的UI操作使用。例如:你可以创建一个当用户把一个彩色图标拖到另一个彩色图标上时,将颜色混合起来的应用。接下来本文将描述关于这个拖放框架的数据移动的内容。
* 概述*
当用户执行一些被当作是开始拖动数据的信号的手势时,一个拖放动作就开始了。作为回应,你的应用程序告诉系统拖动动作开始了。系统回调你的应用程序获取一个代表数据正在被拖动的图形。当用户的手指将这个代表图形(一个拖动阴影)移动到当前布局上时,系统分别发送拖动事件给拖动事件监听器对象,与布局中的View相联系的拖动事件回调方法。一旦用户释放这个拖动阴影,系统就结束拖动操作。
你可以通过在一个类中实现View.OnDragListener,创建拖动事件监听器。然后通过视图对象的setOnDragListener())方法,为视图设置一个拖动事件监听器对象。每个视图对象都可以有一个onDragEvent()) 回调方法。以上两个方法在拖动事件监听器和回调方法 中会详细介绍。
* 注意* :为了简便起见,在接下来的章节中,把程序接收拖动事件称为“拖动事件监听器”,尽管事实上它也有可能是一个回调方法。
当开始一个拖动,就同时包括了正在拖动的数据,以及描述这些数据的元数据作为系统回调的一部分。在拖动过程中,系统会发送拖动事件给布局中的每个视图的拖动事件监听器或回调方法。这些监听器或回调方法可以使用元数据来决定他们是否想要接收那些被拖动的数据。如果用户将数据拖动到一个视图对象上,并且该视图对象的监听器或回调方法事先已经告诉过系统想要接收拖动的数据,那么系统就会把这些数据发送给拖动事件中的监听器或回调方法。
你的应用程序通过调用 startDrag())方法告诉系统开始一个拖动,也就是告诉系统可以开始发送拖动事件了。 startDrag())也会发送你正在拖动的数据。
你可以调用当前布局中任意一个相关联视图的startDrag())方法。系统只会利用视图对象获得进入你布局中的全局设置的权限。
一旦你的应用程序调用startDrag())方法,剩下的过程就是使用系统发送给布局中的视图对象的事件。
拖放过程
拖放过程包括以下四个步骤或状态:
开始
:为了响应用户开始拖动的手势,你的应用程序通过调用 startDrag())方法告诉系统开始一个拖动动作。startDrag())的参数提供被拖动的数据,描述被拖动数据的元数据以及一个绘制拖动阴影的回调方法。
:系统的首次回应是通过回调你的应用程序去获得一个拖动阴影。然后将这个拖动阴影显示在设备上。
:接着,系统发送一个操作类型为 ACTION_DRAG_STARTED的拖动事件给当前布局中的所有视图对象的拖动事件监听器。为了继续接收拖动事件,包括一个可能的拖动事件,拖动事件监听器必须返回true。这样就在系统中注册了一个监听器。只有被注册过的监听器才继续接收拖动事件。这时候,监听器也可以改变他们的视图对象的外观,来表明监听器可以接收一个拖放事件。
:如果拖动事件监听器返回值为false,那么在当前操作中就接收不到拖动事件,直到系统发送一个操作类型为ACTION_DRAG_ENDED的拖动事件。通过发送false,监听器告诉系统它对拖动操作不感兴趣,并且不想接收被拖动的数据。
继续
:用户继续拖动。当拖动阴影和视图对象的边界框相交,系统会发送一个或多个拖动事件给视图对象的拖动事件监听器(如果该事件监听器已经被注册为接收事件)。作为回应,监听器可以选择改变响应拖动事件的视图对象的外观。例如,如果事件表明阴影已经进入了视图的边界框(操作类型为ACTION_DRAG_ENDED),那么监听器就可以高亮视图以作出回应。
释放
:用户在可以接收数据的视图的边界框内释放拖动阴影。系统发送一个操作的类型为 ACTION_DROP拖动事件给视图对象的监听器。这个拖动事件包括调用startDrag())方法传给系统的数据。如果接收释放动作的代码执行成功,那么这个监听器会被期望返回true给系统。
:注意,这一步只会在用户在监听器被注册为接收拖动时事件的视图的边界框内释放这个拖动阴影的情况下才会发生。如果用户在其他情况下释放这个拖动阴影,ACTION_DROP的拖动事件就不会被发送。
终止
:在用户释放拖动阴影并且系统发送出一个操作类型为ACTION_DROP的拖动事件(如果有必要的话)之后,系统发送出一个操作类型为ACTION_DRAG_ENDED的拖动事件来表明这个拖动操作已经结束了。不管用户在哪里释放这个拖动阴影,这个步骤都会发生。这个事件会发送给每一个被注册为接收拖动事件的监听器,即使这个监听器已经接收了ACTION_DROP事件。
上面四步中的每一步都会在 设计一个拖放操作 一文中详细地介绍。
拖动事件监听器和回调方法
一个视图通过实现了View.OnDragListener 的拖动事件监听器或通过它自己的onDragEvent(DragEvent)) 回调方法来接收拖动事件。当系统调用这个这个方法或监听器时,系统传递给他们一个DragEvent对象。
在大多数情况下,你可能会想要使用监听器。当你设计界面时,通常不会继承视图类,但使用回调方法时,为了要重写这个方法,就会迫使你去继承视图类。相比之下,你还可以实现一个监听器类,然后在几个不同的视图对象中使用它。你也可以将这个监听器类作为一个匿名内部类去实现。调用 setOnDragListener())这个方法,就可以为一个视图对象设置监听器。
视图对象可以同时有一个监听器和一个回调方法。如果在这种情况下,系统会首先调用监听器。除非监听器返回的是false,要不然系统不会去调用回调方法。
onDragEvent(DragEvent))方法和View.OnDragListener的结合跟触屏事件的onTouchEvent())与 View.OnTouchListener的结合是相似的。
拖动事件
系统以DragEvent对象的形式发送一个拖动事件。这个对象包括一个告诉监听器在拖放事件中正在发生什么的事件类型。这个对象还包括其他依赖这个事件类型的数据。
监听器调用getAction())这个方法就可以获得这个事件类型。在DragEvent中,还有其他六个可能的变量,分别在表1中列出。
DragEvent对象还包括你的应用程序在调用startDrag())这个方法的时候要传递给系统的那些数据。在这些数据中,有些数据只是对某些特定的事件类型有效。对每个事件类型有效的数据都列在表2中。在 设计一个拖放操作 中详细描述了那些有效的事件。
表1.DragEvent的事件类型
{|style="border-spacing: 0px;margin: 4px 4px; width: 90%; border-left:1px solid #ccc;border-top:1px solid #ccc; "
|-style="background:#DEE8F1; "
! style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px" | getAction()的值
! style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px" | 意义
|- style=" vertical-align:top;"
| style=" border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |
ACTION_DRAG_STARTED
| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |
在应用程序调用 startDrag())并获得一个拖动阴影之后,视图对象的拖动事件监听器就会接收到这个事件类型的事件。
|- style="vertical-align:top;"
| style=" border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px;" |
ACTION_DRAG_ENTERED
| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px;" |
当拖动阴影刚刚进入视图的边界框范围时,视图的拖动事件监听器就会接收到这个action类型的事件。这是当拖动阴影进入视图的边界框范围时监听器所接收到的第一个事件操作类型。如果监听器还行继续为拖动阴影进入视图边界框范围之这个动作接收拖动事件的话,那么必须返回true给系统。
|- style="vertical-align:top;"
| style=" border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px;" |
ACTION_DRAG_LOCATION
| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px;" |
当拖动阴影还在视图的边界框范围中,视图的拖动事件监听器就会在接收到ACTION_DRAG_ENTERED事件之后接收到这个操作类型的事件。
|- style="vertical-align:top;"
| style=" border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px;" |
ACTION_DRAG_EXITED
| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px;" |
当视图的拖动事件监听器接收到ACTION_DRAG_ENTERED这个事件,并且至少接收到一个ACTION_DRAG_LOCATION事件,那么在用户把拖动阴影移除视图的边界框范围之后,该监听器就会在接收到这个操作类型的事件。
|- style="vertical-align:top;"
| style=" border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px;" |
ACTION_DROP
| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px;" |
当用户在视图对象上释放拖动阴影时,该视图对象的拖动事件监听器就会接收到这个类型的拖动事件。这个操作类型只会发送给在回应 ACTION_DRAG_STARTED类型的拖动事件中返回true的那个视图对象的监听器。如果用户释放拖动阴影的那个视图没有注册监听器,或者用户在当前布局之外的任何对象上释放了拖动阴影,那么这个操作类型就不会被发送。
如果释放动作顺利,监听器应该返回true,否则应该返回false。
|- style="vertical-align:top;"
| style=" border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px;" |
ACTION_DRAG_ENDED
| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px;" |
当系统结束拖动动作时,视图对象的拖动事件监听器就会接收到这个类型的拖动事件。这种操作类型不一定是前面有一个ACTION_DROP事件。如果系统发送一个ACTION_DROP,并接收到一个ACTION_DRAG_ENDED操作类型,并不意味着拖动事件的成功。监听器必须调用getResult())方法来获取在回应 ACTION_DROP事件中返回的结果。如果ACTION_DROP事件没有被发送,那么getResult())就返回false。
|}
表2.
{|style="border-spacing: 0px;margin: 4px 4px; width: 90%; border-left:1px solid #ccc;border-top:1px solid #ccc; "
|-style="background:#DEE8F1; "
! style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px" | getAction()的值
! style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px" | getClipDescription()的值
! style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px" | getLocalState()的值
! style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px" | getX()的值
! style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px" | getY()的值
! style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px" | getClipData()的值
! style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px" | getResult()的值
|- style=" vertical-align:top;"
| style=" border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |
ACTION_DRAG_STARTED
| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |
X
| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |
X
| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |
X
| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |
| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |
| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |
|- style=" vertical-align:top;"
| style=" border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |
ACTION_DRAG_ENTERED
| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |
X
| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |
X
| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |
X
| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |
X
| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |
| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |
|- style=" vertical-align:top;"
| style=" border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |
ACTION_DRAG_LOCATION
| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |
X
| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |
X
| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |
X
| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |
X
| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |
| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |
|- style=" vertical-align:top;"
| style=" border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |
ACTION_DRAG_EXITED
| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |
X
| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |
X
| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |
| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |
| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |
| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |
|- style=" vertical-align:top;"
| style=" border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |
ACTION_DROP
| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |
X
| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |
X
| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |
X
| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |
X
| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |
X
| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |
|- style=" vertical-align:top;"
| style=" border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |
ACTION_DRAG_ENDED
| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |
X
| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |
X
| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |
| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |
| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |
| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |
X
|}
如果一个方法不包含对某个特定的操作类型有效的数据,那么就会根据该方法的返回值类型返回null或0.
拖动阴影
在拖动过程中,系统会显示一张用户拖动的图片。对数据移动而言,这张图片代表着那些正在被移动的数据。对其他操作而言,这张图片代表着拖动操作的某些环节。
这张图片就被叫做是一个拖动阴影。你可以通过你声明的View.DragShadowBuilder对象的方法去创建它,然后当你使用startDrag())方法的一部分,系统调用你定义的View.DragShadowBuilder里面的回调方法去获取一个拖动阴影。
View.DragShadowBuilder类有两个构造函数:
::这个构造函数接受你的应用程序中的任意一个视图对象。它将视图对象存储View.DragShadowBuilder对象中,因此在回调过程中,你可以去访问这个构造方法,为你构造一个拖动阴影。构造方法不必和用户选择开始一个拖动的视图对象(如果有的话)相关联。
::如果你使用这个构造方法,不必去继承View.DragShadowBuilder类或覆盖它的方法。默认情况下,你会得到一个与你作为参数传递的那个视图有相同外表的拖动阴影,并且该拖动阴影会居中位于用户接触的屏幕上。
::如果你使用这个构造方法,在View.DragShadowBuilder 对象中没有一个视图对象是有效的(这个字段被设置为null)。如果使用该构造函数,你不必继承View.DragShadowBuilder类或覆盖它的方法,你可以得到一个不可见的的拖动阴影。系统不会给出一个错误。
View.DragShadowBuilder类有两个方法:
::系统在调用了android.view.View.DragShadowBuilder,java.lang.Object,int) startDrag()这个方法之后,立刻回调用这个方法。用这个方法给系统发送拖动阴影的规模和接触点。这个方法有两个参数:
:dimensions
::一个Point对象。拖动阴影的宽为x,高为y。
:touch_point
::一个Point对象。这个接触点应该是拖动过程中,在用户手指之下的拖动阴影的位置。它的X轴坐标为x,Y轴坐标为y。
:在调用了onProvideShadowMetrics())方法之后,系统立刻调用onDrawShadow())这个方法来获取拖动阴影。这个方法只有一个参数,一个Canvas对象,该对象是系统利用你提供给 onProvideShadowMetrics())方法里面的参数构造出来的。利用它可以在提供给你的Canvas对象中绘制你的拖动阴影。
* 设计一个拖放操作*
这个章节会一步一步说明如何开始一个拖动,如何在拖动过程中回应事件,如何回应一个拖动事件以及如何结束一个拖放操作。
开始一个拖动动作
用户用一个拖动的手势开始一个拖动,通常是一个在视图对象上的长按动作。作为回应,应做到以下几点:
- 必要时,为那些已经移动的数据创建一个ClipData 和ClipData.Item对象。作为ClipData这个对象的一部分,在ClipData中提供存储在ClipDescription对象中的元数据。因为一个拖放动作不能代表数据的移动,你可能想要使用null来代替一个实际的数据。
比如:下面这个代码片段说明了如何通过创建一个包含了ImageView的标志或标签的ClipData对象,来回应在ImageView上的一个长按动作。以下就是这些片段,第二个片段说明了如何重写View.DragShadowBuilder这个类中的方法。
// Create a string for the ImageView label
private static final String IMAGEVIEW_TAG = "icon bitmap"
// Creates a new ImageView
ImageView imageView = new ImageView(this);
// Sets the bitmap for the ImageView from an icon bit map (defined elsewhere)
imageView.setImageBitmap(mIconBitmap);
// Sets the tag
imageView.setTag(IMAGEVIEW_TAG);
1 |
...
|
// Sets a long click listener for the ImageView using an anonymous listener object that
// implements the OnLongClickListener interface
imageView.setOnLongClickListener(new View.OnLongClickListener() {
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
// Defines the one method for the interface, which is called when the View is long-clicked
public boolean onLongClick(View v) {
// Create a new ClipData.
// This is done in two steps to provide clarity. The convenience method
// ClipData.newPlainText() can create a plain text ClipData in one step.
// Create a new ClipData.Item from the ImageView object's tag
ClipData.Item item = new ClipData.Item(v.getTag());
// Create a new ClipData using the tag as a label, the plain text MIME type, and
// the already-created item. This will create a new ClipDescription object within the
// ClipData, and set its MIME type entry to "text/plain"
ClipData dragData = new ClipData(v.getTag(),ClipData.MIMETYPE_TEXT_PLAIN,item);
// Instantiates the drag shadow builder.
View.DrawShadowBuilder myShadow = new MyDragShadowBuilder(imageView);
// Starts the drag
v.startDrag(dragData, // the data to be dragged
myShadow, // the drag shadow builder
null, // no need to use local data
0 // flags (not currently used, set to 0)
);
}
|
}
2.下面这个代码片段定义了myDragShadowBuilder 。它为拖动一个TextView创建了一个小的灰色矩形框拖动阴影。
private static class MyDragShadowBuilder extends View.DragShadowBuilder {
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
// The drag shadow image, defined as a drawable thing
private static Drawable shadow;
// Defines the constructor for myDragShadowBuilder
public MyDragShadowBuilder(View v) {
// Stores the View parameter passed to myDragShadowBuilder.
super(v);
// Creates a draggable image that will fill the Canvas provided by the system.
shadow = new ColorDrawable(Color.LTGRAY);
}
// Defines a callback that sends the drag shadow dimensions and touch point back to the
// system.
@Override
public void onProvideShadowMetrics (Point size, Point touch)
// Defines local variables
private int width, height;
// Sets the width of the shadow to half the width of the original View
width = getView().getWidth() / 2;
// Sets the height of the shadow to half the height of the original View
height = getView().getHeight() / 2;
// The drag shadow is a ColorDrawable. This sets its dimensions to be the same as the
// Canvas that the system will provide. As a result, the drag shadow will fill the
// Canvas.
shadow.setBounds(0, 0, width, height);
// Sets the size parameter's width and height values. These get back to the system
// through the size parameter.
size.set(width, height);
// Sets the touch point's position to be in the middle of the drag shadow
touch.set(width / 2, height / 2);
}
// Defines a callback that draws the drag shadow in a Canvas that the system constructs
// from the dimensions passed in onProvideShadowMetrics().
@Override
public void onDrawShadow(Canvas canvas) {
// Draws the ColorDrawable in the Canvas passed in from the system.
shadow.draw(canvas);
}
}
|
* 注意* :记住你不必去继承View.DragShadowBuilder。构造方法View.DragShadowBuilder(View))会创建一个默认的拖动阴影,这个拖动阴影与传递给它的View参数一样大,并且位于以接触点为中心的位置。
回应一个拖动的开始
在拖动过程中,系统将拖动事件分配给当前布局中的视图对象的拖动事件监听器。监听器应该调用getAction())这个方法获取操作类型。在一个拖动开始时,这个方法返回ACTION_DRAG_STARTED。
作为回应一个操作类型为 ACTION_DRAG_STARTED的事件,监听器应该做到以下几点:
1.调用getClipDescription())方法获取ClipDescription。使用在ClipDescription 中的MIME类型的方法查看监听器是否接收被拖动的数据。
如果拖放操作没有代表数据的移动,那么这个步骤就不是必须的。
2.如果监听器可以接收一个拖动,它必须返回true。这样会告诉系统继续发送拖动事件给监听器。如果监听器不接收一个拖动,就会返回false,系统就会停止发送拖动事件直到它发送ACTION_DRAG_ENDED。
注意对于ACTION_DRAG_STARTED事件,以下这些DragEvent的方法都是无效的:getClipData())、 getX())、 getY())和getResult())。
在拖动过程中处理事件
在拖动过程中,作为回应ACTION_DRAG_STARTED拖动事件,监听器返回true来继续接受拖动事件。监听器在拖动过程中接收到的拖动事件类型取决于拖放阴影的位置以及监听器视图的可见性。
在拖动过程中,监听器首先使用拖动事件来决定是否应该改变他们的视图的外观。
在拖动过程中,getAction())返回以下三个变量中的一个:
-
ACTION_DRAG_ENTERED:当接触点(屏幕上位于用户手指下的那个点)进入监听器的视图的边界框范围内时监听器会接收到这个事件。
-
ACTION_DRAG_LOCATION:一旦监听器接收到ACTION_DRAG_LOCATION事件,在它接收到ACTION_DRAG_EXITED事件之前,接触点每移动一次,它都会接收到一个新的ACTION_DRAG_LOCATION事件。方法getX())和getY())会返回接触点的X轴和Y轴的坐标。
-
ACTION_DRAG_EXITED:在拖动阴影不再位于监听器视图的边界框范围之内时,这个事件会被发送给以前接收到ACTION_DRAG_ENTERED事件的监听器。
监听器不必对这些操作类型中的任意一个作出反应。如果监听器返回一个值给系统,它会被忽略掉。下面是应对这些动作类型的一些准则:
-
在回应ACTION_DRAG_ENTERED或者ACTION_DRAG_LOCATION时,监听器可以通过改变视图的外观来表明它将要接收到一个拖动。
-
具有ACTION_DRAG_LOCATION操作类型的事件包含了对getX())和getY())方法有效的数据,相应的接触点的位置。监听器可能可以使用这些信息来改变在接触点的视图的部分的外观。监听器也可以用这些信息来决定用户想要释放拖动阴影的精确位置。
-
在回应ACTION_DRAG_EXITED时,监听器应该重置它在回应ACTION_DRAG_ENTERED或ACTION_DRAG_LOCATION中应用的任何外观的变化。这是在向用户表明视图不再是一个临近被释放的目标。
回应一个释放动作
当用户在应用程序的视图上释放拖动阴影时,并且该视图会事先报告是否可以接收被拖动的内容,系统将拖动事件分发给那个含有 ACTION_DROP操作类型的视图。监听器应做到以下几点:
1.调用getClipData())方法获取最初在startDrag())方法中应用的ClipData对象,并储存之。如果拖放操作没有代表数据的移动,这些都不是必须的。
2.监听器应返回true来表明释放动作已顺利完成,如果没有完成的话,则返回false。这个被返回的值成为ACTION_DRAG_ENDED事件中getResult())方法的返回值。
需要注意的是,如果系统没有发送出ACTION_DROP事件,那么ACTION_DRAG_ENDED事件中getResult())方法的返回值就为false。
对于ACTION_DROP事件来说,在释放动作的瞬间,getX())和getY())方法使用接收释放动作的视图上的坐标系统,返回拖动点的X轴和Y轴的坐标。
系统允许用户在监听器不接收拖动事件的视图上释放拖动阴影。系统允许用户在应用程序UI的空区域或者应用程序之外的区域释放拖动阴影。在以上例子中,系统虽然会发送ACTION_DRAG_ENDED事件,但是不会发送一个ACTION_DROP事件。
回应一个拖动的结束
用户释放了拖动阴影后,系统会立即给应用程序中所有的拖动事件监听器发送ACTION_DRAG_ENDED类型的拖动事件,表明拖动动作结束了。
每个监听器都应该做下列事情:
1.如果监听器在操作期间改变了View对象的外观,那么应该把View对象重置为默认的外观。这是对用户可见的操作结束的指示.
2.监听器能够可选的调用getResult())方法来查找更多的相关操作。如果在响应ACTION_DROP类型的事件中监听器返回了true,那么getResult())方法也会返回true。在其他的情况中,getResult())方法会返回false,包括系统没有发出ACTION_DROP事件的情况.
3.监听器应该给系统返回true。
回应拖动事件:一个例子
所有的拖动事件都会被拖动事件的回调方法或监听器所接收。以下代码片段是一个简单的在监听器中对拖动事件作出反应的示例。
// Creates a new drag event listener
mDragListen = new myDragEventListener();
View imageView = new ImageView(this);
// Sets the drag event listener for the View
imageView.setOnDragListener(mDragListen);
...
protected class myDragEventListener implements View.OnDragEventListener {
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
// This is the method that the system calls when it dispatches a drag event to the
// listener.
public boolean onDrag(View v, DragEvent event) {
// Defines a variable to store the action type for the incoming event
final int action = event.getAction();
// Handles each of the expected events
switch(action) {
case DragEvent.ACTION_DRAG_STARTED:
// Determines if this View can accept the dragged data
if (event.getClipDescription().hasMimeType(ClipDescription.MIMETYPE_TEXT_PLAIN)) {
// As an example of what your application might do,
// applies a blue color tint to the View to indicate that it can accept
// data.
v.setColorFilter(Color.BLUE);
// Invalidate the view to force a redraw in the new tint
v.invalidate();
// returns true to indicate that the View can accept the dragged data.
return(true);
} else {
// Returns false. During the current drag and drop operation, this View will
// not receive events again until ACTION_DRAG_ENDED is sent.
return(false);
}
break;
case DragEvent.ACTION_DRAG_ENTERED: {
// Applies a green tint to the View. Return true; the return value is ignored.
v.setColorFilter(Color.GREEN);
// Invalidate the view to force a redraw in the new tint
v.invalidate();
return(true);
break;
case DragEvent.ACTION_DRAG_LOCATION:
// Ignore the event
return(true);
break;
case DragEvent.ACTION_DRAG_EXITED:
// Re-sets the color tint to blue. Returns true; the return value is ignored.
v.setColorFilter(Color.BLUE);
// Invalidate the view to force a redraw in the new tint
v.invalidate();
return(true);
break;
case DragEvent.ACTION_DROP:
// Gets the item containing the dragged data
ClipData.Item item = event.getClipData().getItemAt(0);
// Gets the text data from the item.
dragData = item.getText();
// Displays a message containing the dragged data.
Toast.makeText(this, "Dragged data is " + dragData, Toast.LENGTH_LONG);
// Turns off any color tints
v.clearColorFilter();
// Invalidates the view to force a redraw
v.invalidate();
// Returns true. DragEvent.getResult() will return true.
return(true);
break;
case DragEvent.ACTION_DRAG_ENDED:
// Turns off any color tinting
v.clearColorFilter();
// Invalidates the view to force a redraw
v.invalidate();
// Does a getResult(), and displays what happened.
if (event.getResult()) {
Toast.makeText(this, "The drop was handled.", Toast.LENGTH_LONG);
} else {
Toast.makeText(this, "The drop didn't work.", Toast.LENGTH_LONG);
};
// returns true; the value is ignored.
return(true);
break;
// An unknown action type was received.
default:
Log.e("DragDrop Example","Unknown action type received by OnDragListener.");
break;
};
};
|
};
相关推荐
在Android开发中,拖放(Drag and Drop)功能是一种常见的用户交互方式,允许用户通过手势将一个对象从一处移动到另一处。这个功能在许多场景下都非常实用,比如整理应用抽屉、移动文件或者在布局中调整控件位置等。...
在"android-dragAndDrop"项目中,我们看到它是针对Android框架的一个测试实例,专注于实现和测试Android应用中的拖放功能。这个项目可能包含了示例代码、练习和教程,帮助开发者理解和掌握如何在Android应用中实现...
在Android开发中,"drag+drop+grid+scroll"是一个重要的功能组合,它涉及到用户界面的交互设计,尤其是对于数据展示和操作的优化。在这个场景下,开发者需要创建一个可以支持拖放(drag & drop)操作、具备网格布局...
"flutter_list_drag_and_drop.zip"这个压缩包文件很可能是包含了一个示例项目,用于演示如何在Flutter应用中实现列表项的拖放操作。 首先,我们需要了解Flutter中的`ListView`组件。`ListView`是Flutter中用于展示...
Android提供了一套完整的Drag and Drop框架,包括DragListener、DragShadowBuilder等类。在开始拖动操作时,需要创建一个DragShadowBuilder实例来定义拖动项的视觉效果,然后调用startDrag()方法开始拖动。在拖动...
首先,Android的Drag and Drop API提供了一个框架,用于在屏幕上移动视图或数据对象。这个API包含几个关键类,如DragEvent、ClipData和DragShadowBuilder。DragEvent表示拖放操作的各个阶段,包括DRAG_ENTERED、DRAG...
在本文中,我们将深入探讨基于Qt库的可拖放(Drag and Drop)软件框架设计。Qt是一个跨平台的C++图形用户界面应用程序开发框架,它提供了丰富的功能和工具,使得开发者能够创建出具有优秀用户体验的桌面和移动应用。...
- ACTION_DROP事件是完成拖放操作的关键,此时需要更新数据源(如ArrayList)的顺序,并通知Adapter数据已改变,以刷新界面显示。 2. **GridView与Adapter**: - GridView是基于Adapter的,它需要一个实现了`...
在Android开发中,拖放(Drag and Drop)技术是一种常见的用户交互方式,它允许用户通过手势将一个视图元素从一处移动到另一处,常用于实现文件管理器、桌面小部件等场景。本资料包"DragAndDropBTWLayouts.zip"可能...
首先,理解Android中的Drag and Drop框架是至关重要的。Android系统提供了DragLayer类来处理拖放事件,以及DragShadowBuilder类用于创建拖动阴影。开发者通常需要创建一个View或ViewGroup,并实现DragEventListener...
5. **Drag and Drop** 功能: - Android 提供了 DragLayer 和 DragController 用于实现拖放操作,这是 Launcher2 中一个重要的特性,允许用户自由调整图标的位置,创建或删除文件夹。 6. **长按菜单和快捷方式**:...
为了提升用户体验,《Pro Android 4》还介绍了如何在应用中添加拖放(Drag and Drop)、2D动画、列表小部件(List Widgets)、触摸屏幕的处理等高级功能。 ### 传感器和硬件交互 书中详述了如何使用传感器API来...
3. **Drag and Drop**: 描述中提到的“可拖动”特性,需要实现Android的拖放(Drag and Drop)API。开发者需要监听触摸事件,处理拖放开始、移动和结束等状态,并更新视图的位置。 4. **适配Android 10.0**: “支持...
首先,让我们深入理解拖放(Drag and Drop)技术。在Android应用中,拖放是一种用户交互模式,用户可以通过触摸屏幕并移动手指来选择一个元素,并将其“拖动”到另一个位置或“放下”。这种功能常见于各种应用场景,...
对于可移动的列表,我们通常需要实现拖放(Drag and Drop)功能。这涉及到对RecyclerView的ItemTouchHelper的使用,通过监听用户的触摸事件,处理拖放操作。在适配器中,我们需要重写`onMove()`和`onSwiped()`方法来...
首先,我们需要了解什么是拖放(Drag and Drop)。在计算机用户界面中,拖放是一种交互技术,允许用户通过鼠标或其他输入设备选择一个对象(如图标或文本),然后将其移动到另一个位置,或者放置到能接收该对象的目标...
1. **拖放操作的实现**:我们可以使用Android的`DragAndDrop`框架来实现拖放功能。当用户长按时,启动拖放操作,并设置拖放的视图为当前选中的item。同时,我们需要监听`ACTION_DRAG_STARTED`、`ACTION_DRAG_ENTERED...
5. **DragAndDrop**: Launcher中的拖放功能涉及`DragController`和`DropTarget`接口,用于处理元素的移动和放置。 6. **Preference**: 对于设置界面,`PreferenceFragment`和`PreferenceScreen`会用来构建用户可以...