Drag and Drop(http://blog.csdn.net/fireofstar/article/details/7408439)

来源:互联网 发布:淘宝鲱鱼罐头神评论 编辑:程序博客网 时间:2024/05/10 14:00

把数据从当前布局中的一个View对象中移到另一个View对象中。这个框架包括:拖拽事件类、拖拽监听器、以及辅助的方法和类。


从实现View.OnDragListener接口的类中创建一个拖动事件监听器对象。用View对象的setOnDragListener()方法把拖动事件监听器对象设置给一个View对象。每个View对象还有一个onDragEvent()回调方法。这两个方法会在“拖拽事件监听器和回调方法”一节中详细介绍。


拖放过程

在拖放过程中有以下四个基本的步骤或状态:

1.  开始

在响应用户的屏幕手势中来开始拖拽,应用程序要调用startDrag()方法来告诉系统开始拖拽。给startDrag()方法提供的参数包括:被拖拽的数据、这个数据的元数据、以及描画拖拽阴影的回调方法。

系统通过响应这个调用,首先返回应用程序需要的拖拽阴影。然后再设备上显示阴影。

接下来,系统会把带有ACTION_DRAG_STARTED类型的拖拽事件发送给当前布局中所有的View对象的拖拽事件监听器,如果要继续接收拖拽事件包括可能的放下事件,View对象的拖拽事件监听器必须返回true。这样就在系统中注册了监听器。只有被注册的监听器能够继续接收拖拽事件。这时,监听器也能够改变它的View对象的外观来表示这个对象的监听器能够接收放下事件。

如果拖拽事件监听器返回false,那么一直到系统发送带有ACTION_DRAG_ENDED类型操作的拖拽事件时,这个事件监听器都不会接收当前操作的拖拽事件。通过发送false返回值,监听器会告诉系统它对这个拖拽操作不感兴趣,并且不会接收被拖拽的数据。

2.  持续

用户持续拖拽过程中,当拖拽阴影跟一个View对象的边框相交时,系统就会发送一个或多个拖拽事件给View对象的拖拽事件监听器(如果它被注册用来接收这些事件的话)。监听器也可以选择改变响应事件的View对象的外观。例如,如果这个事件指示拖拽阴影已经进入到接受拖拽事件的View对象的边框内,监听器就能够通过高亮显示它的View对象来做出反应。

3.  放下

用户在能够接受数据的View对象的边框内释放拖拽阴影,系统就会给View对象的监听器发送一个带有ACTION_DROP操作类型的拖拽事件。这个拖拽事件包含在startDrag()方法调用中传递给系统的数据。如果接受成功,监听器就会返回true给系统。

注意,这个步骤只会在用户放下拖放阴影的View对象(这个对象被注册用于接受这个拖拽事件)中发生,如果用户在其他的任何不接收这个拖拽事件的地方释放了拖拽阴影,就不会有ACTION_DROP拖拽事件发出。

4.  结束

用户释放了拖拽阴影之后,并且如果需要,系统也会发出了带有ACTION_DROP操作类型的拖拽事件,系统就会发出带有ACTION_DRAG_ENDED操作类型的拖拽事件,指示拖拽操作结束了。用户释放拖拽阴影的时机就是解除注册的时机。这个事件会发送给每个被注册用于接受这个拖拽事件的监听器,即使这个监听器收到了ACTION_DROP事件。

拖拽事件监听器和回调方法

View对象既可以用实现View.OnDragListener接口的拖放事件监听器,也可以用View对象的onDragEvent(DragEvent)回调方法来接收拖拽事件。当系统调用这个回调方法或监听器时,都要给它们传递一个DragEvent对象。

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

拖拽影子

在拖拽和放下操作期间,系统会显示一张用户拖动的图片。对于要移动的数据,这张图片就代表了被拖动的数据。对于操作,这张图片就代表了拖动操作的某些外观。

这张图片被叫做拖动影子,使用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对象,并在这个对象中描画拖拽影子。

要改善性能,就要保持拖拽影子要用小的尺寸。对于一个单独的项目,可以使用一个图标,对于多项选择,可以是堆栈中的图标而不是分散在屏幕上的完整的图片。

对拖拽开始的响应

在拖拽操作期间,系统会分发拖拽事件给当前布局中View对象的拖拽事件监听器。监听器应该通过调用getAction()方法对获得的操作类型做出反应。在拖拽开始时,这个方法返回ACTION_DRAG_STARTED.

在对ACTION_DRAG_STARTED操作类型做出的响应中,监听器应该做下列事情:

1.  调用getClipDescription()方法来获得ClipDescription对象,使用ClipDescription对象中的MIME类型方法来判断监听器是否能够接收被拖拽的数据。

如果拖拽操作不代表要移动的数据,这个判断就不是必须的了。

2.  如果监听器能够接受落下事件,它应该返回true。这样就告诉系统可以继续给这个监听器发送拖拽事件。如果不能够接收落下事件,它应该返回false,系统就不再会给这个监听器发送拖拽事件了。

要注意的是针对ACTION_DRAG_STARTED事件,下列DragEvent对象方法不能获取有效的数据:getClipData()getX()getY()getResult()

在拖拽期间处理事件

在拖拽期间,在响应ACTION_DARG_STARTED拖拽事件中返回true的监听器会继续接收拖拽事件。这种类型的拖拽监听器会依赖拖拽期间拖拽影子的位置和监听器的View对象的可见性来接收拖拽事件。

在拖拽期间,监听器主要使用拖拽事件来判断是否应该改变View对象的外观。

拖拽期间,getAction方法会返回下列三个值之一:

1. ACTION_DRAG_ENTERED:当触点(触屏上手指下方的点)进入监听器View对象的边框时,View对象的拖拽监听器就会收到这个事件。

2. ACTION_DRAG_LOCATION:一旦拖拽监听器收到一个ACTION_DRAG_ENTERED事件,并且在收到ACTION_DRAG_EXITED事件之前,每次移动触点时都会收到一个新的ACTION_DRAG_LOCATION事件。getXgetY()方法会返回触点的XY轴坐标。

3. ACTION_DRAG_EXITED:在拖拽影子离开监听器View对象的边框之后,这个事件会发送给之前收到ACTION_DRAG_ENTERED事件的那个监听器。

 

监听器不需要对这些操作类型都做出反应,如果监听器给系统返回了一个值,它就会被忽略。以下是响应这些操作类型的一些指南:

1.  在对ACTION_DRAG_ENTEREDACTION_DRAG_LOCATION事件的响应中,监听器能够改变View对象的外观来指示View对象能够接受放下事件。

2. ACTION_DRAG_LOCATION事件包含了对getX()getY()方法有效的数据,这两个方法的返回值对应了触点的位置。监听器可以使用这个信息来修改触点所在的View对象的外观,也能使用这个信息来判断用户拖放阴影的准确位置。

3. 在对ACTION_DRAG_EXITED事件的响应中,监听器应该重设在响应ACTION_DRAG_ENTEREDACTION_DRAG_LOCATION事件对外观的任何改变。这个事件指示拖放对象已经离开准备放下的目标。

响应放下事件

当用户在应用中的一个View对象上释放了拖拽影子,并且这个View对象是之前报告的能够接收被拖拽内容的那个View对象,系统就会给这个View对象发送一个ACTION_DROP类型的拖拽事件。监听器应该做下列事情:

1.  调用getClipData()方法获得ClipData对象,这个对象在调用startDrag()方法时被初始化并保存在拖拽监听器中。如果拖放操作不移动数据,那么就不需要做这件事;

2.  返回true,指示放下事件被成功处理,否则返回false。对于ACTION_DRAG_ENDED事件,这个返回值就是通过getResult()方法返回的值。

要注意的是,如果系统不发送ACTION_DROP事件,针对对ACTION_DRAG_ENDED事件的getResult()方法调用的返回值是false

系统允许用户在监听器不接收拖放事件的View对象之上释放拖拽影子,也允许用户在应用程序UI的空白区域或应用程序以外的区域释放拖拽影子,这样系统就不会发出ACTION_DROP类型的事件,直接会发出一个ACTION_DRAG_ENDED事件。

响应拖拽结束事件

用户释放了拖拽影子后,系统会立即给应用程序中所有的拖拽事件监听器发送ACTION_DRAG_ENDED类型的拖拽事件,指示拖拽操作结束了。

每个监听器都应该做下列事情:

1.  如果监听器在操作期间改变了View对象的外观,那么应该把View对象重设为默认的外观。这是对用户可见的操作结束的指示;

2.  监听器能够可选的调用getResult()方法来查找更多的相关操作。如果在响应ACTION_DROP类型的事件中监听器返回了true,那么getResult()方法也会返回true。在其他的情况中,getResult()方法会返回false,包括系统没有发出ACTION_DROP事件的情况;

3.  监听器应该给系统返回true

1.  必要时,给要移动的数据创建一个ClipDataClipData.Item对象,作为ClipData对象的一部分,在ClipData对象内部的ClipDescription对象中保存了元数据。因为拖放操作不代表数据的移动,因此可以使用null来代替实际的对象。

例如,以下代码段显示了如何在ImageView对象的长按事件上创建一个包含ImageView对象标签的ClipData对象。

// 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(newView.OnLongClickListener(){

   
// Defines the one method for the interface, which is called when the View is long-clicked
   
publicboolean 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=newClipData.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=newClipData(v.getTag(),ClipData.MIMETYPE_TEXT_PLAIN,item);

   
// Instantiates the drag shadow builder.
   
View.DrawShadowBuilder myShadow=newMyDragShadowBuilder(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{

   
// The drag shadow image, defined as a drawable thing
   
privatestaticDrawable shadow;

       
// Defines the constructor for myDragShadowBuilder
       
publicMyDragShadowBuilder(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
=newColorDrawable(Color.LTGRAY);
       
}

       
// Defines a callback that sends the drag shadow dimensions and touch point back to the
       
// system.
       
@Override
       
publicvoid onProvideShadowMetrics(Point size,Point touch)
           
// Defines local variables
           
privateint 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
       
publicvoid 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=newImageView(this);

// Sets the drag event listener for the View
imageView
.setOnDragListener(mDragListen);

...

protectedclass myDragEventListenerimplementsView.OnDragEventListener{

   
// This is the method that the system calls when it dispatches a drag event to the
   
// listener.
   
publicboolean onDrag(View v,DragEventevent){

       
// Defines a variable to store the action type for the incoming event
       
finalint action=event.getAction();

       
// Handles each of the expected events
       
switch(action){

           
caseDragEvent.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;

           
caseDragEvent.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;

               
caseDragEvent.ACTION_DRAG_LOCATION:

               
// Ignore the event
                   
return(true);

               
break;

               
caseDragEvent.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;

               
caseDragEvent.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;

               
caseDragEvent.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;
       
};

   }

}





原创粉丝点击