Android 7.0 分屏拖拽文字和图片的研究
Posted 骨灵冷
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 7.0 分屏拖拽文字和图片的研究相关的知识,希望对你有一定的参考价值。
一、 前提
1.同一个应用app;
2.不同的activity(A和B)且支持分屏;
3.两个activity共享屏幕(即处于分屏状态,上下排列或左右排列)
二、 实测一
目的: 启动同一个应用的另一个activity,并以分屏的方式共享整个屏幕
为了让A和B共享屏幕,需要让androidManifest文件中A、B对应的activity支持分屏,即设置如下activity标签:
android:resizeableActivity="true”
同时,如果想让A启动B时,需要在A发起跳转B的intent中添加flag:
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT);
这样就可以实现如下效果:
点击“启动另一个ACTIVITY”
三、 实测二
目的:实现分屏共享下,同一个应用中的两个activity进行数据拖拽
具体操作:
在activity A中拖拽名称为“拖拽我吧”这个Button类型的View,然后拖拽到下一个activity的TextView中,拖拽成功之后,发现下一个activity的TextView中的文本变成了button控件中的设置的tag
效果如下:
相关代码如下:
Activity A中实现对应View的拖拽MotionEvent:
//通过button的拖拽执行相应的操作
mButtonDrag.setTag("I am a button");
mButtonDrag.setOnTouchListener(new View.OnTouchListener()
@Override
public boolean onTouch(View view, MotionEvent event)
if (event.getAction() == MotionEvent.ACTION_DOWN)
/*首先,构造ClipData对象,该对象是用来存储拖拽行为时的产生的数据*/
if (Build.VERSION.SDK_INT >= 24)
ClipData.Item item = new ClipData.Item(view.getTag().toString());
String[] mimeTypes = ClipDescription.MIMETYPE_TEXT_PLAIN;
ClipData dragData = new ClipData(view.getTag().toString(), mimeTypes, item);
View.DragShadowBuilder shadow = new View.DragShadowBuilder(mButtonDrag);
view.startDragAndDrop(dragData, shadow, null, View.DRAG_FLAG_GLOBAL);
return true;
else
return false;
);
Activity B中实现目标View接收拖拽后的响应逻辑:
mTextView.setOnDragListener(new View.OnDragListener()
@Override
public boolean onDrag(View view, DragEvent event)
switch (event.getAction())
case DragEvent.ACTION_DROP: mTextView.setText(event.getClipData().getItemAt(0).getText());
break;
default:
break;
return true;
);
注意点:
拖拽行为的触发Action是ACTION_DOWN,所以会与Click事件冲突,经测算会覆盖Click事件。
四、 分析
4.1 分屏拖拽实现分析—-(Activity间)
对分屏拖拽共享数据而言,最后实现的关键是使用View.startDragAndDrop方法,该方法是View的API扩展,详情见:View.startDragAndDrop
在使用View.startDragAndDrop前,需要建立两个对象,分别是ClipData和View.DragShadowBuilder对象,两者的详情参见:ClipData、View.DragShadowBuilder
View.startDragAndDrop
这是一个View的成员方法,其声明如下:
boolean startDragAndDrop (ClipData data,
View.DragShadowBuilder shadowBuilder,
Object myLocalState,
int flags)
根据文档描述可知:当我们的应用程序在调用这个方法的时候,会传递一个View.DragShadowBuilder对象给system,这个玩意儿实际上就是我们在拖拽时产生的投影对象,就是如下所示:
system会回调该类型的onProvideShadowMetrics(Point,Point)方法,这个方法可以帮助获取投影的尺寸信息,接着回调onDrawShadow(Canvas)方法,就在屏幕上绘制出我们看到的View在拖拽时产生的投影效果了。
同时,当拖拽行为发生时,View发出的DragEvent可以被所有可见的View接收到,所以我们的SecondActivity中的可见TextView可以监听到来自MainActivity中button发起的拖拽事件。(为什么一定要是可见的呢?)
当拖拽到目标Activity的目标View时,会计算投影的范围是否落在了目标View的范围中,当为true时,则会触发DragEvent.ACTION_DROP
从而执行响应的逻辑。
● —ClipData—
看一下参数描述:
ClipData: A ClipData object pointing to the data to be transferred by the drag and drop operation.
ClipData就是进行拖拽时需要传递的数据,在该例子中,传递的数据是一个String。
通过查看ClipData的API文档,了解如下:
1.该类型实现了Parcelable串行化接口
2.是对剪切板中的剪切数据的表示—因此是驻留在内存中的,如果是这样,那么就不宜拖拽较大的数据
3.实现原理就是复制+粘贴,copy and paste
4.多种构造方法
其中:
第一个构造函数需要传递lable,mimeTypes和ClipData.Item
第二个构造函数需要传递ClipDescription对象和ClipData.Item
第三个构造函数使用一个已有的ClipData来创建
需要注意的是:ClipDescription的一个构造方法是带lable参数和mimeType参数的,所以构造方法2和构造方法1在某种意义上可以看成是同一个。
5.ClipData的构建离不开ClipData.Item,该类型的构造方法为
本例中,采用的是第一个构造方法,传递一个String即可,实际上,拖拽实现的不仅仅是文本数据,也可以是图片和Intent
如果是图片,需要使用参数为Uri的构造函数
如:ClipData.Item item = new ClipData.Item(Uri.fromFile(new File("/sdcard/xxx.png")));
通过使用Uri的方式,可以实现从一个Activity拖拽文件(包括视频、音频、图片等)到另一个Activity的现象。
具体拖拽图片的例子参见后续例子。
● —View.DragShadowBuilder—
先看一下文档中的构造方法:
只有两种,空的构造方法和带一个View参数的构造方法
所以我们在构建该对象的时候直接传递一个需要拖拽的View对象进去,构造出DragShaldowBuilder对象即可。
● —myLocalState—
根据文档描述,该类型的对象只在同一个activity之间拖拽数据的时候有意义,而在不同activity之间拖拽会返回null,所以本例中直接赋值为null即可。
● —flags—
startDragAndDrop方法的最后一个参数是一个Flag,其取值可以有如下几种:
DRAG_FLAG_GLOBAL:这个flag指的是支持跨window(即分屏状态下)的拖拽
DRAG_FLAG_GLOBAL_PERSISTABLE_URI_PERMISSION:一般和DRAG_FLAG_GLOBAL_URI_READ/WRITE进行配合,保证权限持久性,除非调用Context.revokeUriPermission回收权限
DRAG_FLAG_GLOBAL_PERFIX_URI_PERMISSION:一般和DRAG_FLAG_GLOBAL_URI_READ/WRITE进行配合
DRAG_FLAG_GLOBAL_URI_READ:一般和DRAG_FLAG_GLOBAL配合使用的时候,保证的拖拽的接收端可以有读取的权限
DRAG_FLAG_GLOBAL_URI_WRITE:一般和DRAG_FLAG_GLOBAL配合使用的时候,保证的拖拽的接收端可以有写入的权限
DRAG_FLAG_OPAQUE:这个flag是针对View.DragShadowBuider的,当调用startDragAndDrop方法中带有这个flag时,拖拽产生的投影的不透明的,否则是半透明的,如本例所示。当带有该标记时,产生的效果如下图:
实现拖拽图片的例子
先看效果:
代码实现:
//ImageView的拖拽行为
mImageView.setOnTouchListener(new View.OnTouchListener()
@Override
public boolean onTouch(View view, MotionEvent event)
if (event.getAction() == MotionEvent.ACTION_DOWN)
if (Build.VERSION.SDK_INT >= 24)
//通过Uri创建ClipData.Item对象,Uri是文件地址
ClipData.Item item = new ClipData.Item(Uri.fromFile(new File("/data/data/com.example.songjunmin.mymultiwindow/bitmap.png")));
//通过两个参数的构造函数创建ClipData
String[] mimeTypes = "image/png";
ClipData clipData = new ClipData(new ClipDescription("iamge drag", mimeTypes), item);
//建立shaldow
View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(view);
view.startDragAndDrop(clipData, shadowBuilder, null, View.DRAG_FLAG_GLOBAL);
return true;
else
return false;
);
//ImageView的拖拽监听
mImageView.setOnDragListener(new View.OnDragListener()
@Override
public boolean onDrag(View view, DragEvent event)
switch (event.getAction())
case DragEvent.ACTION_DROP:
mImageView.setImageDrawable(new BitmapDrawable(BitmapFactory.decodeFile(event.getClipData().getItemAt(0).getUri().getPath())));
break;
default:
break;
return true;
);
本例产生的额外注意点:
当从Activity A中拖拽一个元素到Activity B中时,若在Activity B中有两个元素都实现了onDragListener方法,那么两个元素都可以接受从Activity A拖拽过来的元素,所以,在业务角度需要针对该情况进行event中得到的ClipData进行判断。
4.2 拖拽实现分析—-(Activity内)
与分屏拖拽分析原理相近,只不过拖拽时的flag不用为DRAG_FLAG_GLOBAL
以上是关于Android 7.0 分屏拖拽文字和图片的研究的主要内容,如果未能解决你的问题,请参考以下文章
touchstarttouchmovetouchend 实现移动端上的触屏拖拽