Android拖拽功能基础(startDragAndDrop)
介绍
Android系统提供了一个拖拽放下功能,可以实现同一个APP中不同Activity,不同Fragment甚至跨APP的控件拖拽功能。
View.startDragAndDrop
方法会传递一个DragShadowBuilder
对象给系统,系统稍后会调用DragShadowBuilder#onDrawShadow(Canvas)
方法。一旦系统得到DragShadowBuilder对象那么它就开始执行拖动和放下操作,同时会发送事件给当前所有Visible的View对象。接收事件的组件,注册OnDragListener对象来接收拖动事件。
public final boolean startDragAndDrop(ClipData data, DragShadowBuilder shadowBuilder, Object myLocalState, int flags) {}
这个方法有四个参数:
ClipData
ClipData
对象用来存储传送的数据。其中包含一个或多个Item
,Item是事实存储数据的地方。Item的定义如下所示:
public static class Item {
final CharSequence mText;
final String mHtmlText;
final Intent mIntent;
Uri mUri;
}
ClipData
有几个快捷的静态方法帮助快速创建ClipData
对象。
DragShadowBuilder
拖动时展示的阴影,默认的阴影与被拖动的控件一样,显示为半透明。可以根据自己的要求来自定义阴影形状。
View.DragShadowBuilder shadow = new View.DragShadowBuilder(itemView);
Object myLocalState
这个参数可以用作Activity内部一种轻量级的数据传输机制。监听方通过DragEvent#getLocalState()
方法来获取数据。它不能跨Activity,如果在其他Activity调用getLocalState()
方法会返回null
flags
设置为0表示不设置flag。
DRAG_FLAG_GLOBAL 表示可以跨window拖拽,典型的是分屏状态下的拖拽
DRAG_FLAG_GLOBAL_PERSISTABLE_URI_PERMISSION 通常跟DRAG_FLAG_GLOBAL_URI_READ和DRAG_FLAG_GLOBAL_URI_WRITE配合使用,用来在设备重启时保持权限。直到显示地调用Context.revokeUriPermission(这里我也没懂什么意思)
DRAG_FLAG_GLOBAL_PREFIX_URI_PERMISSION 通常跟DRAG_FLAG_GLOBAL_URI_READ和DRAG_FLAG_GLOBAL_URI_WRITE配合使用,the URI permission grant applies to any URI that is a prefix match against the original granted URI。
DRAG_FLAG_GLOBAL_URI_READ 与DRAG_FLAG_GLOBAL一起使用,接收者将能够请求对包含在ClipData对象中的内容URI的读访问。
DRAG_FLAG_GLOBAL_URI_WRITE 与DRAG_FLAG_GLOBAL一起使用,接收者将能够请求对包含在ClipData对象中的内容URI的写访问。
DRAG_FLAG_OPAQUE 使拖动的阴影不透明。
OnDragListener
OnDragListener主要用于监听拖拽事件,当前可见的View都可以进行监听。
recyclerView.setOnDragListener(new View.OnDragListener() {
@Override
public boolean onDrag(View v, DragEvent event) {
switch (event.getAction()) {
case DragEvent.ACTION_DRAG_STARTED:
Log.d("==", "拖拽开始");
break;
case DragEvent.ACTION_DRAG_LOCATION:
float x = event.getX();
float y = event.getY();
Log.d("==", "拖拽View,在监听View中的位置");
break;
case DragEvent.ACTION_DROP:
Log.d("==", "释放");
break;
case DragEvent.ACTION_DRAG_ENDED:
Log.d("==", "拖拽结束");
break;
case DragEvent.ACTION_DRAG_ENTERED:
Log.d("==", "进入监听的View");
break;
case DragEvent.ACTION_DRAG_EXITED:
Log.d("==", "退出监听的View");
break;
default:
break;
}
//这里一定要返回true,返回false只能接受到ACTION_DRAG_STARTED事件,其他事件收不到。
return true;
}
});
这里一定要返回true,返回false只能接受到ACTION_DRAG_STARTED事件,其他事件收不到。
实例
这里我们准备将上面列表中的项拖动到橘黄色的区域。
private class DragClickListener implements View.OnLongClickListener {
private Member data;
private View itemView;
DragClickListener(View itemView, Member data) {
this.data = data;
this.itemView = itemView;
}
@Override
public boolean onLongClick(View v) {
//长按recyclerview中的Item,开始startDragAndDrop
ClipData.Item item = new ClipData.Item(data.getName());
ClipData.Item item2 = new ClipData.Item(String.valueOf(data.getAvatar()));
String[] mimeTypes = {ClipDescription.MIMETYPE_TEXT_PLAIN};
ClipData dragData = new ClipData(data.getName(), mimeTypes, item);
dragData.addItem(item2);
View.DragShadowBuilder shadow = new View.DragShadowBuilder(itemView);
itemView.startDragAndDrop(dragData, shadow, null, View.DRAG_FLAG_GLOBAL);
return false;
}
}
在底部区域监听拖拽事件,当drop在橘黄色区域时添加一个View。
mBottomLayout.setOnDragListener((v, event) -> {
switch (event.getAction()) {
case DragEvent.ACTION_DROP:
ClipData data = event.getClipData();
ClipData.Item item1 = data.getItemAt(0);
ClipData.Item item2 = data.getItemAt(1);
View view = LayoutInflater.from(MainActivity.this).inflate(R.layout.item_member, null, false);
ImageView imageView = view.findViewById(R.id.item_avatar);
TextView textView = view.findViewById(R.id.item_name);
imageView.setImageResource(Integer.parseInt(item2.getText().toString()));
textView.setText(item1.getText());
mBottomLayout.addView(view);
break;
}
//这里一定要返回true,返回false只能接受到ACTION_DRAG_STARTED事件,其他事件收不到。
return true;
});