前言
配合Andorid开发艺术探索笔记食用更佳
View的事件分发分一下四部分,每个部分对事件分发的处理是不一样的,从肉眼可见的顺序编写
首先上个源码
Activity
|
|
首先是交给附属的Window分发,如果返回了true,整个点击事件循环就结束(分发下去),否则交给Activity的onTouchEvent处理。
Window
public abstract boolean superDispatchTouchEvent(MotionEvent event);
是一个抽象方法,真正的实现在PhoneWindow
|
|
事件传给了DecoView,这个就是我们所说的顶级View(也叫ViewRoot),一般是一个ViewGroup
在onCreate中的setContestView就是这个顶级View的一个子View
DecoView(顶级View)
伪代码思想
|
|
源码,ViewGroup的拦截逻辑
|
|
特别注意的是FLAG_DISALLOW_INTERCEPT这个标记位一旦设置后,ViewGroup无法拦截处理action_down之外的点击事件,
在ViewGroup分发事件的时候action_down会重置这个标记位,所以ViewGroup总是会调用onInterceptTouchEvent来询问是否拦截事件
ViewGroup不拦截的时候,源码如下
|
|
大致逻辑如下:
遍历所有的子元素,判断子元素是否能够接收到点击事件,是否在动画化中和点击事件的坐标是否在子元素的区域内
dispatchTransformedTouchEvent实际上就是调用子元素的dispatchTouchEvent方法
上面的child传递的就不是null,所以事件就交给子元素处理了
|
|
如果子元素的dispatchTouchEvent返回true,那么跳出遍历的for循环,mFirstTouchTarget也会被赋值,否则false会继续把事件分发给子元素
addTouchTarget就已经完成了对mFirstTouchTarget的复制,那么ViewGroup就默认拦截同一序列的所有事件
如果遍历的子元素事件没有被合适处理,有两种情况,没有子元素和子元素已经处理了事件,但是dispatchTouchEvent返回了false(子元素在onTouchEvent返回了false)
这就会让ViewGroup自己处理点击事件
|
|
第三个child位null,那么通过前的源码就可以知道会调用super.dispatchTouchEvent(event),这里就会到View的dispathcTouchEvent,事件也就交给了View去完成。
View对点击事件的处理
源码如下:
|
|
有点长,但是单看处理点击事件的只有一点,View无法去分发事件,到头了,只能自己去处理事件。
首先,判断是否设置了OnTouchListener,如果Listener的onTouch方法返回true,那么onTouchEvent就不会被调用
证明了Listener的优先级是高于onTouchEvent的,外界处理事件
onTouchEvent部分源码
|
|
如果view不可用,照样也会消耗点击事件(源码都tm解释给你听了)
假如view有代理,还会执行TouchDelegate的onTouchEvent
|
|
具体的点击处理
|
|
可以看出只要CLIKABLE和LONG_CLICKABLE有一个是true,他就会消耗事件,onTouchEvent返回true,不管Disable庄涛,然后就是action_up发生出发performClick,
设置Listener之后就会调用onClick方法
|
|
其中View的Long Click默认false,ClickAble是由View自己决定的,我们也可自行改变这两个的属性,
分发机制已经梳理得很清晰了。
尾巴
每次进步一点,积累就会成长不少。一开始我对View是拒绝的,很长很复杂,但是通过自己慢慢厘清源码,配合网上或者书上的一些详解就可以啃下这个骨头。