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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值