Android TouchEvent 事件传递机制简单理解
来源:互联网 发布:淘宝上卖军品违法吗 编辑:程序博客网 时间:2024/06/05 14:52
Android 事件传递机制是一个常用的知识点,我们经常会遇到滑动冲突,也经常会遇到 ScrollView 或者 ListView 中嵌套 Button 时点击事件的冲突,这一切都跟事件传递机制有关,所以在看了很多资料后,发现网上访问量很多的帖子也说得不全对,我也就简单记录下对事件传递机制的理解。
参考链接(太多了,列举几个):
http://www.cnblogs.com/Jackwen/p/5239035.html
http://www.cnblogs.com/sunzn/archive/2013/05/10/3064129.html#!comments
http://blog.csdn.net/omg_2012/article/details/7881443
http://blog.csdn.net/xyz_lmn/article/details/12517911
Android 事件传递机制就是当一个触摸事件发生后,从一个窗口到另一个视图,再到另一个视图直到被消费的过程。一次完整的触摸事件是由一个 ACTION_DOWN ,若干(可以为0)个 ACTION_MOVE ,一个ACTION_UP 组成的。
事件传递机制主要涉及到三个方法:
public boolean dispatchTouchEvent(MotionEvent ev);
这个方法是用来分发 MotionEvent 事件的,一般不重写。
return true:事件将分发给当前 view 并由 onTouchEvent() 方法进行消费,事件将停止向下传递。
return false:事件将返还给当前 view 的上一级(可能是 Activity ,也可能是 ViewGroup )的 onTouchEvent() 进行消费。
return super.dispatchTouchEvent(ev):事件自动分发给子 view 的 onInterceptTouchEvent() 方法。
public boolean onInterceptTouchEvent(MotionEvent ev);
这个方法是用来拦截 MotionEvent 事件的,只有 ViewGroup 类才有。
return true:事件将被拦截,并将拦截到的事件交给当前 view 的 onTouchEvent() 方法处理。
return false:事件将被放行,当前view上的事件将传递到子 view 上,并由子 view 的 dispatchTouchEvent() 方法来继续对事件进行分发,如果没有子view了,则会消费该事件,即调用 onTouchEvent() 方法。
return super.onInterceptTouchEvent(ev):默认处理事件的逻辑,与return false相同。
public boolean onTouchEvent(MotionEvent ev);
这个方法是用来响应 MotionEvent 事件的。
return true:事件将被接收并消费。
return false:事件将向上传递,由上一级的 onTouchEvent() 方法处理,并且在一次完整的触摸事件结束前将接收不到下一次事件(“记忆”功能)。
return super.onTouchEvent(ev):默认处理事件的逻辑,与return false相同。
public boolean dispatchTouchEvent(MotionEvent ev) { Log.i("TouchEventDemo", "TouchEventActivity | dispatchTouchEvent --> " + TouchEventUtil.getTouchAction(ev.getAction())); return super.dispatchTouchEvent(ev); } public boolean onTouchEvent(MotionEvent ev) { Log.i("TouchEventDemo", "TouchEventActivity | onTouchEvent --> " + TouchEventUtil.getTouchAction(ev.getAction())); return super.onTouchEvent(ev); }
写一个 ViewGroup 类继承 LinearLayout :
public class TouchEventFather extends LinearLayout { public TouchEventFather(Context context) { super(context); } public TouchEventFather(Context context, AttributeSet attrs) { super(context, attrs); } public boolean dispatchTouchEvent(MotionEvent ev) { Log.i("TouchEventDemo", "TouchEventFather | dispatchTouchEvent --> " + TouchEventUtil.getTouchAction(ev.getAction())); return super.dispatchTouchEvent(ev); } public boolean onInterceptTouchEvent(MotionEvent ev) { Log.i("TouchEventDemo", "TouchEventFather | onInterceptTouchEvent --> " + TouchEventUtil.getTouchAction(ev.getAction())); return super.onInterceptTouchEvent(ev); } public boolean onTouchEvent(MotionEvent ev) { Log.i("TouchEventDemo", "TouchEventFather | onTouchEvent --> " + TouchEventUtil.getTouchAction(ev.getAction())); return super.onTouchEvent(ev); }}
再写一个子View继承TextView:
public class TouchEventChild extends TextView { public TouchEventChild(Context context) { super(context); } public TouchEventChild(Context context, AttributeSet attrs) { super(context, attrs); } public boolean dispatchTouchEvent(MotionEvent ev) { Log.i("TouchEventDemo", "TouchEventChild | dispatchTouchEvent --> " + TouchEventUtil.getTouchAction(ev.getAction())); return super.dispatchTouchEvent(ev); } public boolean onTouchEvent(MotionEvent ev) { Log.i("TouchEventDemo", "TouchEventChild | onTouchEvent --> " + TouchEventUtil.getTouchAction(ev.getAction())); return super.onTouchEvent(ev); }}
其中的TouchEventUtil就是将ACTION_DOWN等行为的值转为文字:
public class TouchEventUtil { public static String getTouchAction(int actionId) { String actionName = ""; switch (actionId) { case MotionEvent.ACTION_DOWN: actionName = "ACTION_DOWN"; break; case MotionEvent.ACTION_MOVE: actionName = "ACTION_MOVE"; break; case MotionEvent.ACTION_UP: actionName = "ACTION_UP"; break; case MotionEvent.ACTION_CANCEL: actionName = "ACTION_CANCEL"; break; case MotionEvent.ACTION_OUTSIDE: actionName = "ACTION_OUTSIDE"; break; } return actionName; }}
activity_main.xml布局中引入自定义的两个控件:
<?xml version="1.0" encoding="utf-8"?><com.qinshou.toucheventdemo.TouchEventFather xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:background="#468AD7" android:gravity="center" android:orientation="vertical"> <com.qinshou.toucheventdemo.TouchEventChild android:id="@+id/childs" android:layout_width="200dp" android:layout_height="200dp" android:layout_gravity="center" android:background="#E1110D" android:gravity="center" android:textSize="30sp" android:text="Hello World!" /></com.qinshou.toucheventdemo.TouchEventFather>
现在我们 return 的都是super.XXX,可以看到发生触摸事件 ACTION_DOWN 时,先执行了 Activity 的dispatchTouchEvent()方法,然后是 TouchEventFather 的dispatchTouchEvent() ,当 dispatchTouchEvent() 方法 return super.dispatchTouchEvent(ev) 时事件自动分发给子 view 的 onInterceptTouchEvent() 方法处理,于是执行了 TouchEventFather 的 onInterceptTouchEvent() 方法,当 onInterceptTouchEvent() 方法 return super.onInterceptTouchEvent(ev)时与 return false 相同,即事件将被放行,并将事件分发给子 view ,并由子 view 继续分发,如果没有子 view 则消费该事件,当前触摸区域没有子 view 了,于是执行了 TouchEventFather 的 onTouchEvent() 方法,当 onTouchEvent() 方法 return super.onTouchEvent(ev) 时与return false相同,即事件将向上传递,由上一级的 onTouchEvent() 方法处理,并且在一次完整的触摸事件结束前将接收不到下一次事件(记忆“功能”),于是返回给 Activity 的 onTouchEvent() 处理,而且由于“记忆”功能,在下一次事件 ACTION_UP 发生时, Activity 并没有分发给 TouchEventFather 而是自己直接执行了 onTouchEvent() ,直到这次完整的事件结束后,下一次再发生 ACTION_DOWN 时, Activity 又会向下分发。
点击一下View,即红色区域,出现的打印结果如下:
可以看到跟刚才一样,执行了 Activity 的dispatchTouchEvent()方法,然后是 TouchEventFather 的 dispatchTouchEvent() 方法,不同的是,当前触摸区域有子 view TouchEventChild,所以 TouchEventChild 又继续分发事件,于是执行了 TouchEventChild 的 dispatchTouchEvent() 方法,然后 TouchEventChild 没有子 view了,所以消费了该事件,同样, return super.onTouchEvent(ev) 时与return false相同,所以又交给父控件 TouchEventFather 执行 onTouchEvent() ,然后 TouchEventFather 也没有消费,交给 Activity 去执行 onTouchEvent() ,然后 TouchEventChild 和 TouchEventFather 直到一次完整的事件结束前,都收不到后面的的事件了。
如果我们修改一下,不是返回默认值呢,比如修改 TouchEventFather 的 onInterceptTouchEvent() 将事件拦截,返回true,再点击红色区域,打印如下:
可以看到,由于 TouchEventFather 将事件拦截了,不会向下分发了,所以 TouchEventChild 无法接收到触摸事件了。
关于上面说的, TouchEventFather 和 TouchEventChild 只能接收到一次触摸事件,根据上面对三个方法的解释,我们将 Activity 、 TouchEventFather 、 TouchEventChild 的 onTouchEvent() 方法都返回true,是否就都能接收到事件了呢?修改代码重新运行,打印如下:
可以看到是这样的。
Android 事件传递机制采用的设计模式是责任链模式,弄懂了这个模式,也就知道它的传递原理了,这里我只是列举了几种情况,至于这三个方法是否应该重写,应该重新哪一个,应该返回 true 还是 false 还是默认值,还得具体情况具体分析,只要我们明白了原理,明白了每个方法的作用,每种返回值的不同效果,相信一般的事件冲突是不难解决的。
- Android TouchEvent 事件传递机制简单理解
- Android TouchEvent事件传递机制
- Android TouchEvent事件传递机制
- Android TouchEvent事件传递机制
- Android TouchEvent事件传递机制
- Android TouchEvent事件传递机制
- Android TouchEvent事件传递机制
- Android TouchEvent事件传递机制
- Android TouchEvent事件传递机制
- Android TouchEvent事件传递机制
- Android TouchEvent事件传递机制
- Android TouchEvent事件传递机制
- Android TouchEvent事件传递机制
- Android TouchEvent事件传递机制
- Android TouchEvent事件传递机制
- Android TouchEvent事件传递机制
- Android TouchEvent事件传递机制
- Android TouchEvent事件传递机制
- ROS 导航 :make_plan (路线规划)
- 安卓网络编程-HTTP协议访问网络
- RC微分与积分电路
- 删除MySQL服务
- linux mysql 简单安装配置
- Android TouchEvent 事件传递机制简单理解
- APUE exercises 14.8 pp532
- makefile 变量的赋值方式
- Linux命令基础20-chmod后面使用数字来表示权限
- express创建服务器路由
- 机器学习实战——python实现knn算法
- unity3D学习笔记之一:基础
- 491
- spring mybatis mapper接口注解方式注入