Android中实现拖拽其实很简单,系统早已经提供了api让我使用,主要用到了View的startDrag(startDragAndDrop API24+) 方法以及OnDragListener

startDrag

先来看下方法介绍:

/*** Starts a drag and drop operation. When your application calls this method, it passes a* {@link android.view.View.DragShadowBuilder} object to the system. The* system calls this object's {@link DragShadowBuilder#onProvideShadowMetrics(Point, Point)}* to get metrics for the drag shadow, and then calls the object's* {@link DragShadowBuilder#onDrawShadow(Canvas)} to draw the drag shadow itself.* <p>*  Once the system has the drag shadow, it begins the drag and drop operation by sending*  drag events to all the View objects in your application that are currently visible. It does*  this either by calling the View object's drag listener (an implementation of*  {@link android.view.View.OnDragListener#onDrag(View,DragEvent) onDrag()} or by calling the*  View object's {@link android.view.View#onDragEvent(DragEvent) onDragEvent()} method.*  Both are passed a {@link android.view.DragEvent} object that has a*  {@link android.view.DragEvent#getAction()} value of*  {@link android.view.DragEvent#ACTION_DRAG_STARTED}.* </p>* <p>* Your application can invoke {@link #startDragAndDrop(ClipData, DragShadowBuilder, Object,* int) startDragAndDrop()} on any attached View object. The View object does not need to be* the one used in {@link android.view.View.DragShadowBuilder}, nor does it need to be related* to the View the user selected for dragging.* </p>* @param data A {@link android.content.ClipData} object pointing to the data to be* transferred by the drag and drop operation.* @param shadowBuilder A {@link android.view.View.DragShadowBuilder} object for building the* drag shadow.* @param myLocalState An {@link java.lang.Object} containing local data about the drag and* drop operation. When dispatching drag events to views in the same activity this object* will be available through {@link android.view.DragEvent#getLocalState()}. Views in other* activities will not have access to this data ({@link android.view.DragEvent#getLocalState()}* will return null).* <p>* myLocalState is a lightweight mechanism for the sending information from the dragged View* to the target Views. For example, it can contain flags that differentiate between a* a copy operation and a move operation.* </p>* @param flags Flags that control the drag and drop operation. This can be set to 0 for no* flags, or any combination of the following:*     <ul>*         <li>{@link #DRAG_FLAG_GLOBAL}</li>*         <li>{@link #DRAG_FLAG_GLOBAL_PERSISTABLE_URI_PERMISSION}</li>*         <li>{@link #DRAG_FLAG_GLOBAL_PREFIX_URI_PERMISSION}</li>*         <li>{@link #DRAG_FLAG_GLOBAL_URI_READ}</li>*         <li>{@link #DRAG_FLAG_GLOBAL_URI_WRITE}</li>*         <li>{@link #DRAG_FLAG_OPAQUE}</li>*     </ul>* @return {@code true} if the method completes successfully, or* {@code false} if it fails anywhere. Returning {@code false} means the system was unable to* do a drag, and so no drag operation is in progress.*/public final boolean startDragAndDrop(ClipData data, DragShadowBuilder shadowBuilder,Object myLocalState, int flags)
复制代码

看到英文就头大?没事,我来翻译解释一下。

启动拖放操作。当应用程序调用此方法时,它将传递一个DragShadowBuilder对象到系统。系统调用此对象的onProvideShadowMetrics(Point, Point)方法获取拖动阴影的参数指标,然后调用onDrawShadow(Canvas)来绘制阴影。一旦系统有了拖动阴影,它就开始拖拽操作,通过将拖拽事件发送到当前可见的应用程序中的所有视图对象。这些视图可以通过设置OnDragListener在或者实现onDragEvent方法接受DragEvent(事件)来响应和拖拽事件。

可以看到有四个参数:

ClipData data

其实就是一个封装数据的对象,通过拖放操作传递给接受者。该对象可以存放一个Item的集合,Item可以存放如下数据:

public static class Item {final CharSequence mText;final String mHtmlText;final Intent mIntent;Uri mUri;
}
复制代码

注意到可以存放Intent,因此,通常可以将参数存入intent,然后通过静态方法直接创建ClipData对象:

ClipData clipData = ClipData.newIntent("label", intent);
复制代码

该数据可以在监听的中的DragEvent获取

ClipData clipData = event.getClipData();
复制代码

简单点说就是可以将一些数据传递给拖拽的接受者,该拖拽其实可以跨Activity的,如果只是同一个Activity可以使用第三个参数传递数据。

DragShadowBuilder shadowBuilder

用于创建拖拽view是的阴影,也就是跟随手指移动的视图,通常直接使用默认即可生成与一个原始view相同,带有透明度的阴影:

View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(v);
复制代码

Object myLocalState

当你的拖拽行为是在同一个Activity中进行时可以传递一个任意对象,在监听中可以通过{@link android.view.DragEvent#getLocalState()}获得。如果是跨Activity拖拽中无法访问此数据,getLocalState()将返回null。

int flags

控制拖放操作的标志。因为没有标志可以设置为0,flag标志拖动是否可以跨越窗口以及一些访问权限(需要API24+)。

了解了方法参数含义,接下来就是启用拖拽了,通常会通过长按来触发拖拽:

iv.setOnLongClickListener(new View.OnLongClickListener() {@Overridepublic boolean onLongClick(View v) {View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(v);v.startDrag(null, shadowBuilder, null, 0);//震动反馈v.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING);return true;}});
复制代码

开始拖拽后还要有来接受这些拖拽事件,这就需要OnDragListener了。

OnDragListener

OnDragListener是在View中定义的接口,用于响应拖拽事件,可以通过View的setOnDragListener 方法设置监听,有点类似于点击事件。

public interface OnDragListener { boolean onDrag(View v, DragEvent event);
}
复制代码

设置监听,实现onDrag(View v, DragEvent event)方法,其中View是设置该监听的view,DragEvent是拖拽事件,可以通过event.getAction() 获取具体事件类型,这和TouchEvent非常类似,具体事件类型有如下几种:

fl_blue.setOnDragListener(new View.OnDragListener() {@Overridepublic boolean onDrag(View v, DragEvent event) {//v 永远是设置该监听的view,这里即fl_blueString simpleName = v.getClass().getSimpleName();Log.w(BLUE, "view name:" + simpleName);//获取事件int action = event.getAction();switch (action) {case DragEvent.ACTION_DRAG_STARTED:Log.i(BLUE, "开始拖拽");break;case DragEvent.ACTION_DRAG_ENDED:Log.i(BLUE, "结束拖拽");break;case DragEvent.ACTION_DRAG_ENTERED:Log.i(BLUE, "拖拽的view进入监听的view时");break;case DragEvent.ACTION_DRAG_EXITED:Log.i(BLUE, "拖拽的view离开监听的view时");break;case DragEvent.ACTION_DRAG_LOCATION:float x = event.getX();float y = event.getY();long l = SystemClock.currentThreadTimeMillis();Log.i(BLUE, "拖拽的view在监听view中的位置:x =" + x + ",y=" + y);break;case DragEvent.ACTION_DROP:Log.i(BLUE, "释放拖拽的view");break;}//是否响应拖拽事件,true响应,返回false只能接受到ACTION_DRAG_STARTED事件,后续事件不会收到return true;}});
复制代码

此处通过event.getX(); event.getY(); 获取的x,y是手指(也即是被拖拽view的中心点)在监听view的位置。

释放手指会触发ACTION_DRAG_ENDED 事件,如果此时被拖拽的view正好在监听的view中,则会先触发ACTION_DROP 事件。

可以同时有多个view设置拖拽监听接受事件,我给红色和蓝色view都设置了OnDragListener,然后拖动Android图片到蓝色区域后释放,可以看到日志如下:

03-09 14:53:54.518 12937-12937/com.huburt.app.androiddrag I/RED: 开始拖拽
03-09 14:53:54.518 12937-12937/com.huburt.app.androiddrag I/BLUE: 开始拖拽
03-09 14:53:55.689 12937-12937/com.huburt.app.androiddrag I/BLUE: 拖拽的view进入监听的view时
03-09 14:53:55.689 12937-12937/com.huburt.app.androiddrag I/BLUE: 拖拽的view在BLUE中的位置:x =111.0,y=2.0
03-09 14:53:55.870 12937-12937/com.huburt.app.androiddrag I/BLUE: 拖拽的view在BLUE中的位置:x =112.0,y=23.0
03-09 14:53:56.014 12937-12937/com.huburt.app.androiddrag I/BLUE: 释放拖拽的view
03-09 14:53:56.017 12937-12937/com.huburt.app.androiddrag I/RED: 结束拖拽
03-09 14:53:56.017 12937-12937/com.huburt.app.androiddrag I/BLUE: 结束拖拽
复制代码

现在我们已经可以把Android图片拖出来,但是还不能把它放入目标view,其实也挺简单的,只需要在ACTION_DROP事件做一些处理即可:

         case DragEvent.ACTION_DROP:Log.i(BLUE, "释放拖拽的view");ImageView localState = (ImageView) event.getLocalState();FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);layoutParams.topMargin = (int) event.getY() - localState.getWidth() / 2;layoutParams.leftMargin = (int) event.getX() - localState.getHeight() / 2;((ViewGroup) localState.getParent()).removeView(localState);fl_blue.addView(localState, layoutParams);break;
复制代码

这里因为是在同一个Activity中,我是将拖拽的view直接传递过来了,当然也可以只传递图片,然后在接收的view中重新new一个imageview现实图片。

运行一下就可以看到view可以拖拽到目标位置了。

可能文字描述不是特别清楚,请看demo

Android拖拽详解相关推荐

  1. react-draggable实现拖拽详解

    react-draggable 属性 常用属性 属性列表 事件列表 举例 首先安装 react-draggable 实现移动 希望小编写的能够帮助到你

  2. 《Android游戏开发详解》——第1章,第1.6节函数(在Java中称为“方法”更好)...

    本节书摘来自异步社区<Android游戏开发详解>一书中的第1章,第1.6节函数(在Java中称为"方法"更好),作者 [美]Jonathan S. Harbour,更 ...

  3. JMessage Android 端开发详解

    JMessage Android 端开发详解 目前越来越多的应用会需要集成即时通讯功能,这里就为大家详细讲一下如何通过集成 JMessage 来为你的 App 增加即时通讯功能. 首先,一个最基础的 ...

  4. 《Java和Android开发实战详解》——2.5节良好的Java程序代码编写风格

    本节书摘来自异步社区<Java和Android开发实战详解>一书中的第2章,第2.5节良好的Java程序代码编写风格,作者 陈会安,更多章节内容可以访问云栖社区"异步社区&quo ...

  5. Android事件流程详解

    Android事件流程详解 网络上有不少博客讲述了android的事件分发机制和处理流程机制,但是看过千遍,总还是觉得有些迷迷糊糊,因此特地抽出一天事件来亲测下,向像我一样的广大入门程序员详细讲述an ...

  6. Android Studio 插件开发详解二:工具类

    转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/78112856 本文出自[赵彦军的博客] 在插件开发过程中,我们按照开发一个正式的项 ...

  7. 《Android游戏开发详解》一2.16 区分类和对象

    本节书摘来异步社区<Android游戏开发详解>一书中的第2章,第2.16节,作者: [美]Jonathan S. Harbour 译者: 李强 责编: 陈冀康,更多章节内容可以访问云栖社 ...

  8. Android Framework系统服务详解

    Android Framework系统服务详解 操作环境 系统:Linux (Ubuntu 12.04) 平台:高通 Android版本:5.1 PS: 符号...为省略N条代码 一.大致原理分析 A ...

  9. android屏幕适配详解

    android屏幕适配详解 官方地址:http://developer.android.com/guide/practices/screens_support.html 一.关于布局适配建议 1.不要 ...

最新文章

  1. RDS for MySQL 5.7 备份恢复为本地实例
  2. struts2静态方法和动态方法调用
  3. vs2005常用调试快捷键 (转载)
  4. 恶补sql知识(一)
  5. [Android]对MVC和MVP的总结
  6. zImage内核镜像解压过程详解
  7. UVa 10118 免费糖果(记忆化搜索+哈希)
  8. linux ssh升级回退,Linux-SSH升级及回退方案
  9. java继承案例分析,Java day5【第十四章】继承案例分析
  10. 主控芯片测试软件,主控芯片检测工具MyDiskTest的使用教程的详解【图文】
  11. ArcGIS学习笔记-1.功能-1.1裁剪
  12. zmq Pub-Sub 模式
  13. TSC2004 电阻式触摸屏控制器驱动问题
  14. 计算机语言收入排名,全球人均收入排名美元_计算机语言收入排名
  15. 【NLP】huggingface阶段性学习小结
  16. 微信 考勤 php,JavaScript_微信企业号开发之微信考勤Cookies的使用,在上篇文章给大家介绍了微信 - phpStudy...
  17. 谁说NTFS不支持UEFI启动的?启动U盘放不了超过4G的文件怎么办?Server2016 Win10 U盘UEFI启动制作方法
  18. 齐岳提供的双核金属铱配合物黏度探针C10((df-ppy)2Ir(bpy)(CH2)10(bpy)Ir(btph)22+)-
  19. fork后父子进程共享资源
  20. 网页中嵌入电视直播代码

热门文章

  1. DPDK使用linux drivers(二十九)
  2. 2 模版_轻量html模版渲染库 cJinja
  3. axios vue 回调函数_Vue 02 —— Vue 入门小案例~使用 Axios 中的GET、POST请求
  4. 微软职位内部推荐-Senior Software Engineer_Azure
  5. [ORACLE错误]oracle 不能更新 PL/SQL 点击“edit data”报“ these query results are not updateable”...
  6. 一起谈.NET技术,Visual Studio 2010 中的代码约定设置
  7. 在虚拟主机中无法实现缩放等交互
  8. 怎么挪动_你真的懂iPhone上的小圆点怎么玩吗
  9. impala连接使用方法
  10. asp.net ajax控件工具集 AutoCompleteExtender控件