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对象,两者的详情参见:ClipDataView.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 实现移动端上的触屏拖拽

Android 7.0的系统变化

Android 7.0都有哪些新功能 Android 7.0新特性汇总

iOS 14借鉴锤子OS?感受下

flutter实现文字识别之图片拖拽选框选取截取文字

android五子棋游戏资讯阅读大学课程表地图拖拽检测小说搜索阅读app等源码