用 Drag and Drop API 实现锤子 "One Step"
昨天有不少非 Android 开发者朋友质疑我写文章的动机。对某些读者的困扰我表示遗憾,这里改一下引起争议标题。
本文只讨论实现类 "One Step" 的拖拽分享的原生 Android 实现,不讨论这个功能是 Android 内置还是『锤子先做出来』。非 Android 开发、或者对技术实现内容不感兴趣、或者想讨论 『体验』的可以一同去 如何评价锤子 M1 中的 One Step 功能? - 锤子科技 讨论。
---------------------------------------------------
昨天老罗发布了锤子新款手机,内置的 "One Step" 功能引爆全场,挑剔的知乎也是一片叫好。
那么,实现 "One Step" 功能真的需要去买一台锤子手机么?答案也许不是这样的:
Android 从 API 11 (3.0) 开始就支持一个 API 叫做 "Drag and Drop",可能不少开发者已经在 App 中已经使用过了。官方链接在此:https://developer.android.com/guide/topics/ui/drag-drop.html 这个 API 可以让你极其容易的把页面里面的一个元素拖拽到其他元素上,甚至是其他 Apps 里 (如 Split Screen 的情况下)。利用这个 API 很容易配合 Android 的 Intent 分享机制实现 "One Step"。
以下是核心代码:
"One step" 部分:
* 启动一个 service/foreground service,如果你对此不了解,可以参阅 https://developer.android.com/guide/components/services.html
private static final int FLAG_TOUCHABLE = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
private static final int FLAG_NOT_TOUCHABLE = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
...
WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
mParams = new WindowManager.LayoutParams();
mParams.format = PixelFormat.TRANSPARENT;
mParams.gravity = Gravity.END;
mParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
mParams.width = WindowManager.LayoutParams.MATCH_PARENT;
//mParams.width = ...;
mParams.flags = FLAG_NOT_TOUCHABLE;
mView = new YourCustomView(this);
mView.setOnDragListener(new View.OnDragListener() {
@Override
public boolean onDrag(View v, DragEvent event) {
switch (event.getAction()) {
case DragEvent.ACTION_DRAG_STARTED:
//drag started
mView.setShow(true);
mParams.flags = FLAG_TOUCHABLE;
wm.updateViewLayout(mView, mParams);
break;
case DragEvent.ACTION_DRAG_ENDED:
//drag ended
mView.setShow(false);
mParams.flags = FLAG_NOT_TOUCHABLE;
wm.updateViewLayout(mView, mParams);
break;
case DragEvent.ACTION_DROP:
//drop received
share(event.getClipData(), event.getX(), event.getY());
break;
}
return true;
}
});
wm.addView(mView, mParams);
注意一点:为了能够获取 DragEvent.ACTION_ENTER, ACTION_EXIT, ACTION_DROP,这个 window 必须为 touchable 的,但是直接设置 touchable 会阻挡触摸事件,所以需要在 onDragListener 里面动态调整 FLAG。拖拽的元素包含的可供共享的数据在 event.getClipData() 里,另外可以获得 Drop event 的 x、y 坐标值来判断分享到哪个 app (这里略)。之后就简单了,大家去用微信的 sdk 也好,用标准 intent,6.0 以上的 Direct share 也好,都能很好的实现。
第三方客户端:
无需任何 SDK,直接调用 View::startDrag 或者 View::startDragAndDrop 方法就可以。
mView.setOnLongClickListener(() -> {
mView.startDrag(
ClipData.newPlainText("Label", "Content"),
shadowBuilder,
null,//这里可以不要,是应用内部拖拽用的
View.DRAG_FLAG_GLOBAL //设置为全局,这样可以拖动到应用外
)
});
ClipData 可以是 Plain Text 也可以是 html 文件,图片,url 等。
效果图:
效果视频,需要科学上网:
(知乎不支持视频和 GIF ,有需求的话我可以把视频搬到墙内)
总结:
如果锤子是用标准的 Drag And Drop api 的话,大家不用等到春季开源,现在就可以开工了,整个实现无需 root 无需专用系统,简单方便,还免费外带支持分屏拖拽数据。
如果锤子是 300 个昼夜自起炉灶,所谓开源最后只能锤子手机独享,那我也不知道应该怎么评价了。。。
祝读者玩 Android 玩的愉快。