Drag and Drop
随着Android拖/放框架,可以允许用户将数据从一个视图使用图形拖动移动到另一个查看当前布局和下降的手势。该框架包括一个拖放事件类,拖累听众和辅助方法和类。
虽然该框架的主要设计用于数据移动,你可以将其用于其他UI操作。例如,您可以创建当用户拖动过另一个图标颜色图标颜色混合的应用程序。本主题的其余部分,但是,描述了数据移动方面的框架。
概观
一个拖放操作开始时用户进行,你承认作为一个信号,开始拖动数据的一些姿态。对此,您的应用程序告诉拖动正在启动系统。系统调用返回到您的应用程序来获得数据的表示被拖动。当用户的手指在当前布局移动这表示(“拖影”),系统会发送拖动事件与布局中的视图对象关联的拖动事件侦听器对象和拖曳事件的回调方法。一旦用户释放拖动阴影,系统结束拖动操作。
您从实现View.OnDragListener类创建一个拖动事件侦听器对象(“监听器”)。您可以设置拖动事件侦听器对象与View对象的setOnDragListener()方法的视图。每个视图对象也有一个onDragEvent()回调方法。这两种中更详细的部分中的拖动事件监听器和回调方法说明。
注意:为了简单起见,下面的部分是指接收拖动事件为“拖拽事件侦听器”的例行程序,即使它实际上可能是一个回调方法。
当您开始拖动,既包括正在移动的数据和元数据描述这个数据作为调用系统的一部分。在拖动期间,系统会发送拖拽事件拖拽事件侦听器或布局的每个View的回调方法。听众或回调方法可以使用元数据来决定他们是否愿意当它降到接受数据。如果用户在一个视图对象丢弃数据,以及查看对象的监听器或回调方法以前告诉它要接受降系统,那么系统中的数据发送到一个拖动事件侦听器或回调方法。
您的应用程序告诉系统通过调用startDrag()方法来启动一个拖累。这告诉系统开始发送拖动事件。该方法还发送您所拖动的数据。
你可以调用的startDrag()在当前布局中连接的任何视图。该系统只使用视图对象来获取在布局访问全局设置。
一旦你的应用程序调用的startDrag(),该过程的其余部分将使用该系统发送到视图对象在当前布局事件。
拖/放过程
基本上有四个步骤或状态的拖放过程:
入门
为了响应用户的手势开始拖累,应用程序调用的startDrag()来告诉系统启动一个拖累。参数的startDrag()提供到被拖动数据,元数据该数据,并用于绘制拖影的回调。
该系统首先通过调用返回到您的应用程序来获得一拖影响应。然后,它显示设备上的拖影。
接下来,系统发送到ACTION_DRAG_STARTED拖动事件侦听器在当前布局中的所有视图对象的动作类型拖曳事件。要继续接收拖动事件,包括可能的放置事件,一拖事件监听器必须返回true。这将注册与系统的监听器。只有注册听众继续接收拖动事件。此时,听众也可以改变他们的查看对象的外观表明听众可以接受的下降事件。
如果拖拽事件侦听器返回false,那么将不会收到拖动事件当前操作,直到系统发送动作类型ACTION_DRAG_ENDED拖曳事件。通过发送假,听众告诉系统,这是不感兴趣的拖动操作,并且不希望接受拖动的数据。
继续
用户可以继续拖动。由于拖影相交的视图对象的边框,系统会发送一个或多个拖动事件查看对象的拖动事件侦听器(如果注册的接收事件)。聆听者可以选择改变响应事件视图对象的外观。例如,如果该事件表明拖影已进入视图(动作类型ACTION_DRAG_ENTERED)的边框,听众可以通过突出其查看反应。
下降
用户释放一种观点认为可以接受数据的边界框内拖影。该系统发送查看对象的监听器与动作类型ACTION_DROP拖曳事件。拖动事件包含传递给系统调用的startDrag()启动该操作的数据。监听器预计将布尔值true返回系统如果接受降码成功。
需要注意的是,如果用户删除了一个视图,其监听器注册接收拖动事件的边界框内拖影这一步只发生。如果用户释放在任何其他情况下拖动阴影,则发送没有ACTION_DROP拖拽事件。
截至
后用户释放拖动阴影,并且系统发送出后(如果需要)与操作类型ACTION_DROP拖拽事件时,系统发出一个拖拽事件与动作类型ACTION_DRAG_ENDED以指示拖动操作已经结束。无论在哪里,用户发布的拖影这样做。事件被发送到每一个被注册以接收拖动事件侦听器,即使听者接收的ACTION_DROP事件。
每个这些四个步骤进行更详细的部分设计一个拖放操作说明。
拖动事件侦听器和回调方法
A浏览接收到任何实现View.OnDragListener或与其onDragEvent(的dragEvent)回调方法拖动事件监听拖动事件。当系统调用该方法或监听器,它传递给他们一个DragEvent对象。
你可能会想使用监听器在大多数情况下。当你设计的用户界面,你平时不继承视图类,但为了要覆盖的方法使用回调方法迫使你这样做。相比较而言,你可以实现一个监听器类,然后用几个不同的视图对象使用它。您还可以实现它作为一个匿名内嵌类。要设置视图对象的监听器,调用setOnDragListener()。
你可以同时拥有监听器和查看对象的回调方法。如果出现这种情况,系统将首先调用监听程序。除非监听器返回false系统不会调用回调方法。
所述onDragEvent(的dragEvent)方法View.OnDragListener和组合类似于与触摸事件中使用的onTouchEvent()和View.OnTouchListener的组合。
拖动事件
该系统发出一个拖动事件的DragEvent对象的形式。该对象包含动作类型,告诉什么在拖/放过程中发生的监听器。所述对象包含的其他数据,这取决于操作类型。
为了让操作类型,侦听器调用的getAction()。有六种可能的值,通过DragEvent类常量定义。这些列于表1。
该DragEvent对象还包含您的应用程序提供给系统调用的startDrag数据()。有些数据只对特定的操作类型有效。这是有效的每个动作类型的数据总结在表2中还详细地,这就是它在部分设计一个拖放操作有效的事件说明。
表1的dragEvent动作类型
的getAction()值含义
ACTION_DRAG_STARTED View对象的拖动事件侦听器接收应用程序调用的startDrag只是此事件后,动作类型(),并得到一拖影。
ACTION_DRAG_ENTERED View对象的拖动事件侦听器接收到这个事件动作类型时拖影刚刚进入视图的边框。这是当拖影进入边界框听者接收第一事件动作类型。如果听众想继续接收拖动事件对于此操作,它必须返回布尔真系统。
它收到ACTION_DRAG_ENTERED事件之后,同时拖动阴影仍是观的边框内ACTION_DRAG_LOCATION View对象的拖动事件侦听器接收到这个事件操作类型。
它接收ACTION_DRAG_ENTERED后ACTION_DRAG_EXITED View对象的拖动事件侦听器接收到这个事件动作类型的至少一个ACTION_DRAG_LOCATION事件,之后用户移动视图的边框外拖影。
当用户释放了View对象拖影ACTION_DROP View对象的拖动事件侦听器接收到这个事件操作类型。这个动作类型只发送到一个视图对象的侦听器,如果听众响应ACTION_DRAG_STARTED拖动事件返回布尔值true。如果用户释放上的视图,其侦听未注册在拖影不发送此动作类型,或者如果用户释放对任何不是当前布局的一部分的拖影。
监听预计,如果它成功处理下降返回布尔值true。否则,它应该返回false。
ACTION_DRAG_ENDED View对象的拖动事件侦听器接收当系统结束拖动操作此事件操作类型。此动作类型不必由一个ACTION_DROP事件之前。如果系统发送一个ACTION_DROP,接收ACTION_DRAG_ENDED动作类型并不意味着下拉操作成功。监听器必须调用的getResult()来获取是响应ACTION_DROP返回的值。如果没有发送的ACTION_DROP事件,那么的getResult()返回false。
通过操作类型表2.有效的dragEvent数据
getAction() value |
getClipDescription() value |
getLocalState() value |
getX() value |
getY() value |
getClipData() value |
getResult() value |
---|---|---|---|---|---|---|
ACTION_DRAG_STARTED |
X | X | X | |||
ACTION_DRAG_ENTERED |
X | X | 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 |
// 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); ... // Sets a long click listener for the ImageView using an anonymous listener object that // implements the OnLongClickListener interface imageView.setOnLongClickListener(new View.OnLongClickListener() { // 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.DragShadowBuilder 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) ); } }
private static class MyDragShadowBuilder extends View.DragShadowBuilder { // 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); } }
// 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.OnDragListener { // 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; } // Returns false. During the current drag and drop operation, this View will // not receive events again until ACTION_DRAG_ENDED is sent. return false; 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; case DragEvent.ACTION_DRAG_LOCATION: // Ignore the event return true; 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; 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; 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; // An unknown action type was received. default: Log.e("DragDrop Example","Unknown action type received by OnDragListener."); break; } return false; } };
getAction() value |
getClipDescription() value |
getLocalState() value |
getX() value |
getY() value |
getClipData() value |
getResult() value |
---|---|---|---|---|---|---|
ACTION_DRAG_STARTED |
X | X | X | |||
ACTION_DRAG_ENTERED |
X | X | 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 |