在Android 3.0 以前的版本,拖放一个试图需要使用触摸(Touch)事件,而且拖动到指定的区域还需要判断坐标是否落到这一区域,很麻烦。从Android 3.0以后,Android SDK直接支持视图的拖放操作。
拖放操作需要经历的4种状态。
开始拖动
通过调用View.startDrag方法,可以让视图处于可拖动的状态,这时用户可以用手指(虚拟机当然是鼠标啦)将视图在屏幕上拖动。在视图开始拖动的时候,还会使用一种拖动阴影(Drag Shadow) 技术(View.DragShadowBuilder对象),以便使拖动的图像与原图形不同。
OnDragListener.onDrag 就是处理拖动操作的方法。方法原型:
public boolean onDrag(View view,DragEvent event);
其中view参数表示当前拖动的视图,event表示拖动过程中的各种信息,其中DragEvent.getAction方法最重要,该方法可以获取具体的拖动状态,如果处于拖动状态,则返回DragEvent.ACTION_DRAG_STARTED。
onDrag方法必须返回true ,如果false,表示当前视图不能拖动。
拖动进行时
这个状态是动态的,当视图进入目标区域,目标区域的onDrag方法就会被调用,这时DragEvent.getAction方法会返回DragEvent.ACTION_DRAG_ENTERED。
放下视图
一旦视图在目标区域放下,这时DragEvent.getAction方法会返回DragEvent.ACTION_DROP,表示视图已经放在了目标区域的某个位置,然后就需要在onDrag方法中做进一步的处理。
结束拖放
当视图放下后(用户松开了手指,拖动阴影消失),不管视图放在了目标区域外,还是目标区域内,系统都会向所有可以监听拖放动作的视图发送DragEvent.ACTION_DRAG_ENDED动作。如果处于目标区域内,发送ACTION_DRAG_ENDED之前,系统会单独向该目标区域内发送ACTION_DROP动作。也就是说只要用户松开正在拖动的视图,DragEvent.getAction方法就一定会返回DragEvent.ACTION_DRAG_ENDED.因此,目标区域接收到DragEvent.ACTION_DRAG_ENDED时不一定是视图放到了目标区域,很可能在目标区域外,所以处理放下动作时要使用DragEvent.ACTON_DROP ,而不要使用DragEvent.ACTION_DRAG_ENDED。
拖动阴影
拖动阴影直接使用View.DragShadowBuilder 类 也可以继承View.DragShadowBuilder 类,实现自定义的拖动阴影类。直接使用则拖动的样式与原图像完全一样,只是左上角偏移了一点。如果要自定义阴影类,一般只需要实现View.DragShadowBuilder类的俩个方法:onProvideShadowMetrics和onDragShow。前者用于生成拖动阴影图像(Bitmap对象),后者用于在画布(Canvas)上画出拖动的阴影图像.
不多说了,还是上代码吧。
//自定义拖动阴影 public class MyDragShadowBuilder extends View.DragShadowBuilder { //拖动阴影的区域 private static Drawable shadow; //存储新绘制的拖动阴影图像 private static Bitmap newBitmap; //可以是任何View对象 public MyDragShadowBuilder(View v) { super(v); shadow = new ColorDrawable(Color.LTGRAY); } //绘制拖动阴影图像,也就是实例化newBitmap变量 @Override public void onProvideShadowMetrics(Point size, Point touch) { int width, height; //拖动阴影图像的宽度和高度,比原图大50% width = (int) (getView().getWidth() * 1.5); height = (int) (getView().getHeight() * 1.5); //设置拖动阴影图像绘制的区域 shadow.setBounds(0, 0, width, height); //设置宽度和高度 size.set(width, height); //设置手指在拖动阴影图案的位置 touch.set(width / 2, height / 2); //判断传人的View对象是否为ImageView对象,下面要获取ImageView控件中的图像资源 if (getView() instanceof ImageView) { //getView方法返回的值就是构造方法传人的v参数值 ImageView imageView = (ImageView) getView(); //获取ImageView控件的Drawable对象 Drawable drawable = imageView.getDrawable(); //根据ImageView控件显示的图像资源(Bitmap对象) Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap(); //根据拖动阴影图像的尺寸创建一个新的可绘制的Bitmap图像 newBitmap = Bitmap.createBitmap(width, height, Config.ARGB_8888); //Canvas与Bitmap关联 Canvas canvas = new Canvas(newBitmap); //将原图绘制在画布上(图像尺寸放大50%)。这里的画布只是与newBitmap绑定的 完全独立的,现在还没有正式将图像绘制在拖动阴影图像上。实际上是将bitmap放大50%,然后绘制在newBitmap上 canvas.drawBitmap(bitmap, new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()), new Rect(0, 0, width, height), null); } } @Override public void onDrawShadow(Canvas canvas) { // 讲图像正式的绘制在拖动阴影图像上。 canvas.drawBitmap(newBitmap, 0, 0, new Paint()); } }
public class FirstDragDropActivity extends Activity implements OnDragListener { //目标区域的视图 private FrameLayout dragdropRegion; //要拖动的视图 private ImageView imageview; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_first_drag_drop); dragdropRegion = (FrameLayout) findViewById(R.id.framelayout_dragdrop_region); imageview = (ImageView) findViewById(R.id.imageview); dragdropRegion.setOnDragListener(this); //为要拖动的视图设置长按单击事件监听器 imageview.setOnLongClickListener(new View.OnLongClickListener() { //长按拖动 public boolean onLongClick(View v) { //创建拖动阴影对象 View.DragShadowBuilder myShadow = new MyDragShadowBuilder( imageview); //开始拖动时,startDrag方法的第一个参数值是一个ClipData类型的对象用于传递剪贴板数据,可以为null v.startDrag(null, myShadow, null, 0); return true; } }); } @Override public boolean onDrag(View view, DragEvent event) { int action = event.getAction(); switch (action) { //开始拖动 case DragEvent.ACTION_DRAG_STARTED: System.out.println("drag_started"); break; //进入目标区域 case DragEvent.ACTION_DRAG_ENTERED: System.out.println("drag_entered"); break; //在目标区域移动 case DragEvent.ACTION_DRAG_LOCATION: System.out.println("drag_location x=" + event.getX() + " y=" + event.getY()); break; //离开目标区域 case DragEvent.ACTION_DRAG_EXITED: System.out.println("drag_exited"); break; //在目标区域放下ImageView控件 case DragEvent.ACTION_DROP: System.out.println("drag_drop"); //创建一个新的ImageView控件(从image.xml布局资源文件创建) ImageView imageView = (ImageView) getLayoutInflater().inflate( R.layout.image, null); LayoutParams layoutParams = new LayoutParams( LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); //由于FrameLayout不能直接使用坐标设置子视图的位置,所以这里使用边距 layoutParams.leftMargin = (int) (event.getX()); layoutParams.topMargin = (int) (event.getY()); //将要ImageView控件添加到FrameLayout中,完成一次复制 dragdropRegion.addView(imageView, layoutParams); break; //放下ImageView控件 case DragEvent.ACTION_DRAG_ENDED: System.out.println("drag_ended"); break; default: return false; } return true; } }
布局文件
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <FrameLayout android:id="@+id/framelayout_dragdrop_region" android:layout_width="match_parent" android:layout_height="300dp" android:background="#F00" /> <ImageView android:id="@+id/imageview" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_marginTop="50dp" android:src="@drawable/ic_launcher" /> </LinearLayout>
<ImageView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_marginTop="50dp" android:src="@drawable/ic_launcher" />
运行程序,长按图像到红色区域,放下就会绘制到放下的位置。