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
该的getAction(),describeContents(),writeToParcel()和toString()方法总是返回有效的数据。


如果一个方法不包含有效的数据为特定的操作类型,则返回null或0,这取决于其结果类型。

投影

在一个拖放操作,系统将显示用户拖动图像。对于数据移动,这个形象代表被拖动的数据。对于其他操作,图像表示拖动操作的某些方面。


该图像被称为拖影。你和你声明的View.DragShadowBuilder对象的方法创建它,然后把它传递给系统,当您使用的startDrag启动一拖()。作为其的startDrag()反应的一部分,该系统调用你已经在View.DragShadowBuilder定义为获得​​一拖影的回调方法。


该View.DragShadowBuilder类有两个构造函数:


View.DragShadowBuilder(查看)
此构造函数接受你的任何应用程序的视图对象。构造函数存储在View.DragShadowBuilder对象视图对象,所以回调期间,您可以为您建造拖影访问它。它不具有要与显示有关(如果有的话),该用户选择,以便开始拖曳操作。
如果您使用此构造函数,你不必延长View.DragShadowBuilder或覆盖它的方法。默认情况下,你会得到一个具有美观,你作为参数传递,其中用户触摸屏幕的位置居中下查看相同的拖影。


View.DragShadowBuilder()
如果使用此构造,没有查看对象在View.DragShadowBuilder对象(字段设置为null)可用。如果使用此构造函数,你不延长View.DragShadowBuilder或覆盖它的方法,你会得到一个无形的阻力阴影。该系统不给出错误。
该View.DragShadowBuilder类有两个方法:


onProvideShadowMetrics()
该系统调用这个方法调用的startDrag后立即()。用它来发送到系统拖影的尺寸和触摸点。该方法有两个参数:
外形尺寸
的Point对象。拖影那张宽x和它的高度去y中。
接触点
的Point对象。触摸点是在拖影,应该是在拖动期间用户的手指下中的位置。其X位置变为以x和Y轴的位置去y中
onDrawShadow()
调用onProvideShadowMetrics()系统调用onDrawShadow后立即()来获得拖影本身。该方法只有一个参数,一个Canvas对象,该系统从你onProvideShadowMetrics提供的参数()用它来绘制拖影在所提供的Canvas对象构造。
为了提高性能,你应该保持阻力的大小影小。对于单个项目,您可能需要使用一个图标。对于多重选择,您可能需要使用图标堆叠中的,而不是全面的图像分布在屏幕上。


设计一个拖放操作


本节说明一步一步如何启动一个拖累,如何在拖动过程中响应事件,如何应对drop事件,以及如何结束拖放操作。


开始一拖


在用户开始与拖动动作一拖,平时长按,在一个视图对象。作为回应,你应该做到以下几点:


根据需要,创建一个用于数据的ClipData和ClipData.Item移动。作为ClipData对象的一部分,存储在ClipData内ClipDesc​​ription对象供给的元数据。对于拖放不代表数据移动操作时,您可能要改为null,则使用一个实际的对象。
例如,下面的代码片段展示了如何通过创建一个包含的ImageView的标签或标签的ClipData对象长按响应上ImageView的。下面这个片段中,接下来的片段展示了如何覆盖View.DragShadowBuilder方法:

// Create a string for the ImageView labelprivate static final String IMAGEVIEW_TAG = "icon bitmap"// Creates a new ImageViewImageView imageView = new ImageView(this);// Sets the bitmap for the ImageView from an icon bit map (defined elsewhere)imageView.setImageBitmap(mIconBitmap);// Sets the tagimageView.setTag(IMAGEVIEW_TAG);    ...// Sets a long click listener for the ImageView using an anonymous listener object that// implements the OnLongClickListener interfaceimageView.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)            );    }}

下面的代码片段定义了我的DragShadowBuilder它为拖动一个TextView作为一个小的灰色矩形一拖影:

  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);        }    }

注:请记住,你不必延长View.DragShadowBuilder。构造函数View.DragShadowBuilder(查看)创建一个默认拖影是这样的大小传递给它,用拖影中心的接触点查看参数相同。


回应一拖开始


在拖动操作时,系统分派拖动事件在当前布局视图的对象的拖动事件侦听器。听众应该调用的getAction()来获取操作类型反应。在拖动开始时,此方法将返回ACTION_DRAG_STARTED。


为了应对与ACTION_DRAG_STARTED动作类型的事件,侦听器应做到以下几点:


呼叫getClipDesc​​ription()来获得ClipDesc​​ription。使用MIME类型方法ClipDesc​​ription,看看听众能接受被拖动的数据。
如果拖拽操作不表示数据移动,这可能不是必要的。
如果听众可以接受的下降,但应返回true。这告诉系统继续发送拖动事件监听器。如果它不能接受的下降,它应该返回false,系统将停止发送拖动事件,直到它发出ACTION_DRAG_ENDED。
注意,对于一个ACTION_DRAG_STARTED事件,这些下面的dragEvent方法是无效的:getClipData(),的getX(),的getY(),和的getResult()。


在拖动过程中处理事件


在拖动期间,在应对ACTION_DRAG_STARTED拖动事件返回真正的监听器继续接收拖动事件。在拖动过程中侦听器能够接收的类型拖动事件取决于拖影的位置和听者的视图的可见性。


在拖动期间,听众主要是使用拖放事件,以决定他们是否应该改变他们查看的外观。


在拖动期间,的getAction()返回三个值中的一个:


ACTION_DRAG_ENTERED:侦听器接收到这个时候接触点(用户的手指下方的屏幕上的点)已经进入听者的视图的边框。
ACTION_DRAG_LOCATION:一旦侦听器能够接收ACTION_DRAG_ENTERED事件,它收到一个AACTION_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。监听器应做到以下几点:


呼叫getClipData()来获取)最初在通话供给的startDrag(该ClipData对象存储。如果拖拽操作不表示数据移动,这可能不是必要的。
返回布尔值true,表明下跌被成功处理,或者布尔值false,如果事实并非如此。返回的值将成为一个ACTION_DRAG_ENDED事件)通过的getResult(返回的值。
请注意,如果系统没有发送一个ACTION_DROP事件,的getResult()的值用于ACTION_DRAG_ENDED事件为假。
用于ACTION_DROP事件,的getX()和的getY()返回X和在下降的时刻的拖动点的Y位置,使用该接收到的下拉视图的坐标系。


该系统确实允许用户发布在查看其侦听器未接收拖动事件拖影。它也将允许用户发布应用程序的用户界面的空白区域,或在您的应用程序以外的地区拖影。在所有这些情况下,系统不发送与操作类型ACTION_DROP一个事件,虽然它发送一个ACTION_DRAG_ENDED事件。


回应一拖到底


用户释放拖动阴影紧接着,系统会发送拖曳事件的所有应用程序中的拖动事件侦听器,具有ACTION_DRAG_ENDED的动作类型。这表明拖动操作已经结束。


每个听者应做到以下几点:


如果听者在操作过程中改变了查看对象的外观,它应该重新查看到其默认外观。这是一个视觉指示,即该操作是通过用户。
侦听器可以随意调用的getResult(),以了解更多的操作。如果听众响应返回真实的动作类型ACTION_DROP的事件,那么的getResult()将返回布尔值true。在其他情况下,的getResult()返回布尔值false,包括在系统没有发出一个ACTION_DROP事件的任何情况。
监听器应该返回布尔值true系统。
响应拖动事件:一个例子


所有拖动事件最初是由您的拖动事件的方法或监听器接收。下面的代码片段是反应拖动事件侦听器的一个简单的例子:

// Creates a new drag event listenermDragListen = new myDragEventListener();View imageView = new ImageView(this);// Sets the drag event listener for the ViewimageView.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

更多相关文章

  1. 滑轮控件研究二、GestureDetector的深入研究
  2. Android(安卓)用户界面---拖放(Drag and Drop)(一)
  3. Android(安卓)5.0 Lollipop 短彩信接收流程
  4. android中使用ViewPager实现图片左右拖动
  5. Android中Button点击事件实现的三种方式总结及Demo演示
  6. Android(安卓)XML解析Pull方式———解析网络数据示例
  7. android的Touch事件的消费机制
  8. Android监听事件的回调机制
  9. Android(安卓)让人又爱又恨的触摸机制(一)

随机推荐

  1. Android APK安装过程及原理详解
  2. Android中设置关键字高亮的方法
  3. android计时demo源代码
  4. 新建Cocos2dx-Android工程
  5. android 动态添加View
  6. Android高级工程师成长路线
  7. Android :Process xxxxx (pid xxxxx) has
  8. Android在Log中打印出当前的调用栈
  9. Android自学笔记(番外篇):全面搭建Linux环境
  10. Android中回调接口的使用