Android Touch事件派发流程源码分析

分native侧事件派发到java侧和Framework派发事件到UI,流程看源码即可,此处不赘叙,

Native侧派发事件的干活类图如下:
 
Framework侧派发事件的类图如下:
从Activity.dispatchTouchEvent开始,Action_Down事件派发的时序如下:
分析Android 5.0源码可知,ViewGroup的事件派发是一个后序遍历树的递归过程,在Action_Down事件的处理中做了两个事情:
1.onInterceptTouchEvent判断是否需要拦截事件,若否,执行2 
2.递归遍历在点击区域内的子节点,查找touchTarget,将其标记为当前根节点ViewGroup的mFirstTouchTarget,整个流程下来,会在递归返回时为Touched节点生成一个touchTarget对象,在后序事件中直接依据touchTarget执行其dispatchTouchEvent,忽略其他节点。
示例如下:
对应的View Tree如下:
 
若CustomView在onTouchEvent返回true,则生成的touchTarget链表为
DecorView->Content->…->RelativeLayout->CustomViewGroup->CustomView
具体实现逻辑在ViewGroup.dispatchTouchEvent和addTouchTarget中:
 3.遍历顺序为后序遍历,遍历核心逻辑如下:child==null时对应的就是当前子树的根节点处理逻辑,
若child !=null,则遍历其子节点的dispatchTouchEvent,找到目标节点后,会执行顶级父类View的dispatchTouchEvent,其核心逻辑如下:
 
目标节点的判定条件需满足onTouch/onTouchEvent返回true,而在View.onTouchEvent中,若满足以下条件,便返回true:
即View设置了android:clickable/android:longclickable为true,我们看下为何View.setOnClickListener会达到等价效果,
其实现如下:

综上,Action_Down事件是通过后序遍历View Tree查找满足上述条件的节点作为Target,借此创建Target Link,供Action_Move、Action_UP事件使用。

下面分析下Action_Up事件的处理流程,ViewGroup.dispatchTouchEvent会检查mFirstTouchTarget,若mFirstTouchTarget==null,则表明该ViewGroup的子节点均不在Action_Down事件处理生成的target链表上,执行其View.dispatchTouchEvent,若从DecorView开始的整颗树均不在target链表上,则最后由Activity.onTouchEvent兜底,若mFirstTouchTarget不为null,执行touchTarget的dispatchTouchEvent:

 View.onTouchEvent在Action_Up时的实现如下,可以看到在Action_UP事件时异步执行了onClick函数

下面分析Action_Cancel事件的产生场景、对TouchTarget的影响:
分析源码可知,ViewGroup的onInterceptTouchEvent在Action_Down的后续事件中均会执行,
假如其针对Action_Move情况下,对事件进行了截断,即onInterceptTouchEvent=true,则会向其touchTarget
派发Action_Cancel事件,并清除当前ViewGroup的touchTarget,这样后续Action_Move、Action_Up便也不会派发下去了,自己消费掉。
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action); // restore action in case it was changed
} else {
intercepted = false;
}

final boolean cancelChild = resetCancelNextUpFlag(target.child)
|| intercepted;
if (dispatchTransformedTouchEvent(ev, cancelChild,
target.child, target.pointerIdBits)) {
handled = true;
}

if (canceled
        || actionMasked == MotionEvent.ACTION_UP
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
resetTouchState();
这样的场景下,原先的TouchTarget的Action_UP事件便没有执行机会,相应的onClickListener也便不会执行了。

posted on 2015-10-12 20:53  熊猫观星  阅读(3607)  评论(0编辑  收藏  举报

导航