您的位置:首页 > 移动开发 > Android开发

Android 事件传递

2015-11-01 16:45 344 查看
网上有不少关于View 的事件传递的相关文章,其实写的都还不错, 这里我自己写来整理一下,关于View 事件传递的相关文章, 来加深自己对于事件的处理

这几天写博客,发现, 不仅能够加深自己对已知识点的记忆, 同时也能深入到一些平时在工作中被忽略的点.

ok 下面进入正题,

View 关于事件有一下 3个方法:
boolean dispatchTouchEvent
boolean setOnTouchListener
boolean onTouchEvent

正常情况下,事件的传递顺序是:
dispatchTouchEvent —> setOnTouchListener —> onTouchEvent
这里需要注意的是, 如果设置了 OnTouchListener ,那个事件会先走
OnTouchListener 里面的 onTouch 方法, 如果事件没有被吃掉才会走 onTouchEvent 方法

ok 在来看看 ViewGroup 的 事件相关的方法:
boolean dispatchTouchEvent
boolean onInterceptTouchEvent
setOnTouchListener
boolean onTouchEvent
正常情况下得事件 传递的顺序是:
dispatchTouchEvent —> onInterceptTouchEvent —> setOnTouchListener —> onTouchEvent

ok 上面知识一个非常简单的一个事件传递的 过程:
下面再来详细讲一讲:
首先我可以看到
boolean dispatchTouchEvent
boolean onInterceptTouchEvent
boolean setOnTouchListener. onTouch
boolean onTouchEvent
这几个方法 的返回值 都是 boolean 类型, 这一点很重要.
其实 这个返回值 代表这个方法是否吃掉了这个事件:

其实当我点按屏幕的适合, 事件最开始是给Activity的
Activity 有如下两个事件相关的方法:
dispatchTouchEvent
onTouchEvent

事件一开始传递给 Activity 的 dispatchTouchEvent 方法, 然后在这个事件分发,
在把事件传递给 相对应的View, 这里的过程相对复杂,以后再说,
现在在来看看 事件传递ViewGroup 或View 之后的相关过程

dispatchTouchEvent 方法的作用, 分发事件, 即使该事件 落到屏幕上, 到底应该分发给那个View 去执行

在ViewGroup.dispatchTouchEvent 过程中,
首先会 会先去执行 onInterceptTouchEvent 方法,
如果 onInterceptTouchEvent 返回true 说明, 这个事件被 ViewGroup吃掉了
那么, 事件就不会在传递到子View 上去了.
事件会传递到ViewGroup的 OnTouchListener. onTouch 或 onTouchEvent 方法上去
如果 OnTouchListener. onTouch 返回true 表示事件被吃掉了
如果 OnTouchListener. onTouch 返回false或OnTouchListener == null 那么事件就会传递到 onTouchEvent 方法上去

当 onInterceptTouchEvent 返回 false ,说明该事件没有被拦截, 那么 ViewGroup就会一次遍历子View ,

先看 down 事件
看该事件是不是落在相应的子View 上
在ViewGroup.dispatchTouchEvent方法中 循环是这样开始的

for (int i = childrenCount - 1; i >= 0; i--) {

循环是从最顶层的View 开始执行的
然后判断这个事件事件的这个 xy 坐标是否是在这个View 上和这个View是否能够接受事件
, 如果不在这个子view 上, 或这个子view 不能接收事件,
则continue 下一个
如果这 这down事件在这个子View上面, 那个则会去执行这个子view 的dispatchTouchEvent方法
如果该子view的 dispatchTouchEvent返回true 那么
则会跳出循环.
接下里的所有 down up 事件都会传递到 该子View 上去,

如果一次循环遍历下来, 子View dispatchTouchEvent 方法都返回false
那么说明, 事件 子View 都不要, 那么事件接下来 就会传到 Viewgroup 的 OnTouchListener. onTouch 或 onTouchEvent 方法上了,
如果 onTouchEvent 也 不要这个方法, 那么 最后这个事件会传递到 Activity 上去的 onTouchEvent 的.

ok 接下来看看 View 的dispatchTouchEvent方法
其实在 在该方法里面页很简单, 就是吧事件直接 传递给 OnTouchListener. onTouch 和 onTouchEvent
看view 本身到底需不需要这个事件, 如果 OnTouchListener. onTouch 或 onTouchEvent 返回true 那么
本身的 dispatchTouchEvent方法也会返回 true

上面可能讲的也挺乱的, 本来想拿源码一起来讲解, 但是在 5.1.1 的源码 在这几个方法加了很多一些 相关的判断逻辑, 有点长, 讲起来也比较麻烦.

现在提炼出一些 结理论直接说吧,
1. 如果一个View 想要吃掉这个事件, 那个你必须在 onTouch 或 onTouchEvent 方法中返回true 才行
2. 如果你吃掉了Down 事件,那么后续所有时间按都会传递给你 知道这次事件结束
3. 如果你不想 子View 吃掉该事件, 那么在 onInterceptTouchEvent 方法中返回 true
4.
我们经常会遇到这样的情况, 就是如果这个事件是普通的 click事件,那么就不拦截, 如果是滑动就拦截
那么这就需要在 ViewGroup的onInterceptTouchEvent 中判断这个事件 是否是滑动,
如果这个事件是滑动, 其实最开始Down事件已经传递给了 子View 中去了 ,
那么当 ViewGroup 判断该事件为滑动时 拦截时, 子View 会受到一个cancel事件且手续的事件都收不到了,
同时 事件将传递给ViewGroup 的 onTouch 或 onTouchEvent 方法:

上面的情况是 有以下log:
0 表示down事件, 2 表示move事件, 1 表示up事件 , 3 是 cancel 事件
11-11 16:09:43.491 12643-12643/com.imczy.toucheventdemo D/CustomScrollViewGroup: onInterceptTouchEvent ev = 0
11-11 16:09:43.491 12643-12643/com.imczy.toucheventdemo D/MyButtom: dispatchTouchEvent event = 0
11-11 16:09:43.491 12643-12643/com.imczy.toucheventdemo D/MyButtom: onTouchEvent event = 0
11-11 16:09:43.532 12643-12643/com.imczy.toucheventdemo D/CustomScrollViewGroup: onInterceptTouchEvent ev = 2
11-11 16:09:43.533 12643-12643/com.imczy.toucheventdemo D/MyButtom: dispatchTouchEvent event = 2
11-11 16:09:43.533 12643-12643/com.imczy.toucheventdemo D/MyButtom: onTouchEvent event = 2
11-11 16:09:43.563 12643-12643/com.imczy.toucheventdemo D/CustomScrollViewGroup: onInterceptTouchEvent ev = 2
11-11 16:09:43.563 12643-12643/com.imczy.toucheventdemo D/MyButtom: dispatchTouchEvent event = 2
11-11 16:09:43.563 12643-12643/com.imczy.toucheventdemo D/MyButtom: onTouchEvent event = 2
11-11 16:09:43.594 12643-12643/com.imczy.toucheventdemo D/CustomScrollViewGroup: onInterceptTouchEvent ev = 2
11-11 16:09:43.594 12643-12643/com.imczy.toucheventdemo D/MyButtom: dispatchTouchEvent event = 3
11-11 16:09:43.595 12643-12643/com.imczy.toucheventdemo D/MyButtom: onTouchEvent event = 3
11-11 16:09:43.610 12643-12643/com.imczy.toucheventdemo D/CustomScrollViewGroup: onTouchEvent ev = 2
11-11 16:09:43.626 12643-12643/com.imczy.toucheventdemo D/CustomScrollViewGroup: onTouchEvent ev = 2
11-11 16:09:43.642 12643-12643/com.imczy.toucheventdemo D/CustomScrollViewGroup: onTouchEvent ev = 2
11-11 16:09:43.658 12643-12643/com.imczy.toucheventdemo D/CustomScrollViewGroup: onTouchEvent ev = 2
11-11 16:09:43.894 12643-12643/com.imczy.toucheventdemo D/CustomScrollViewGroup: onTouchEvent ev = 2
11-11 16:09:43.898 12643-12643/com.imczy.toucheventdemo D/CustomScrollViewGroup: onTouchEvent ev = 1


可以看到 上面 MyButtom dispatchTouchEvent onTouchEvent 接收到的事件是:
0 > 2 > 2 (这里CustomScrollViewGroup 中判断了事件为滑动, 那么拦截掉时间按, 下面 就受到了cancel事件)> 3

下面在看看 CustomScrollViewGroup onInterceptTouchEvent 受到的事件:
0 > 2 >2
当判断为 滑动 拦截掉事件后, 事件就传递到了传递到 ViewGroup本身 就不需要在调用 onInterceptTouchEvent 判断是否拦截事件了

接下来看看 CustomScrollViewGroup onTouchEvent 事件
(判断为滑动拦截后 )2 > 2 > 2 > 1
指示在拦截事件以后 才开始受到 move 事件 和 up 事件

5. 当View 不可见时 ,事件不会传递给它
6. view 即使设置 setEnabled(false); 依然可以接收处理

7. 如果不想 ViewGroup 拦截掉事件 ,那么可以在 view 的onTouchEvent 的 ACTION_DOWN中调用
mParent.requestDisallowInterceptTouchEvent(true);
这样 父View就不会拦截事件了

如下代码:

@Override
public boolean onTouchEvent(MotionEvent event) {
LogUtil.d(TAG, "onTouchEvent event = " + event.getAction());

switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if (mParent != null) {
mParent.requestDisallowInterceptTouchEvent(true);
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
if (mParent != null) {
mParent.requestDisallowInterceptTouchEvent(false);
}
break;
}
return super.onTouchEvent(event);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: