事件分发知识点
事件分类
事件 | 简介 |
---|---|
ACTION_DOWN | 手指 初次接触到屏幕 时触发 |
ACTION_MOVE | 手指 在屏幕上滑动时触发,会多次触发 |
ACTION_UP | 手指 离开屏幕 时触发 |
ACTION_CANCEL | 事件 被上层拦截 时触发 |
ACTION_OUTSIDE | 表示用户触碰超出了正常的UI边界 |
ACTION_POINTER_DOWN | 有一个非主要的手指按下了 |
ACTION_POINTER_UP | 一个非主要的手指抬起来了 |
相关方法
类型 | 相关方法 | Activity | ViewGroup | View |
---|---|---|---|---|
事件分发 | dispatchTouchEvent | √ | √ | √ |
事件拦截 | onInterceptTouchEvent | X | √ | X |
事件消费 | onTouchEvent | √ | X | √ |
事件分发流程
流程图
源码分析(时序图)
事件冲突
冲突原因
根本原因就是事件分发到某个对象时,该对象处理了不该处理的事件或者没有处理到本该自己处理的事件。
- 例如提前拦截子view的事件,返回 true。
- 又或者子view消费了事件,仅完成了自己的操作,没有让父view 处理其负责的事件。
解决冲突
-
内部拦截法
父 view 在
onInterceptTouchEvent
可以放行含有子view该处理的事件
到子view,子view在dispatchTouchEvent
发现是自己该处理的,请求父view不要拦截,发现是父view该处理事件,就让父view拦截该事件 -
外部拦截法
父view在
onInterceptTouchEvent
判断是否为自己该处理的事情,是的话就拦截,返回true。不是的话就放行或默认处理,看情况
其他
// 不允许父view调用onInterceptTouchEvent方法
// ViewCompat.setNestedScrollingEnabled(this, true);
getParent().requestDisallowInterceptTouchEvent(true);
事件分发流程再看
四个重要方法:
- dispatchTouchEvent
- onInterceptTouchEvent 在分发流程如果调用这方法返回 true,则时间会被拦截
- onTouchEvent
- setOnTouchListener
requestDisallowInterceptTouchEvent(true)
子 view 禁止 parent 以及它的祖先拦截触摸事件
onTouchListener
中的onTouch
方法每次都会先于view本身的onTouchEvent
调用,且有优先消费权
事件的记忆机制
含义:窗口内的某个view消费了事件(onTouchEvent返回true),则后续事件会直接发给此view的onTouchEvent,不会再经过其他view的onTouchEvent。
原理:路径记忆是用分层记忆实现的。每个viewgroup都是分发的关键节点,其维护一个TouchTarget链表、记录哪个子view消费该事件。上层只关注其直接子节点。
事件的截获机制
如果截获ACTION_DOWN事件(返回true),则事件不会再分发给子view。
dispatchTouchEvent
if(onInterceptTouchEvent)
return this.onTouchEvent
else
return child.onTouchEvent
两种情况可截获:
- ACTION_DOWN事件一定可截获
- 非ACTION_DOWN事件,若子类消费了事件,且自身标志位允许截获,则可截获。
事件的分裂机制
含义:
当某个view消费一次事件(ACTION_DOWN)后,后续点击到其旁系view的多指触摸事件将被分裂为单独的事件(ACTION_DOWN),且其旁系view可消费、记忆此单独事件。
Android 3.0 以上默认开启此机制,可在主题中配置activity和window关闭此功能。