上篇文章概述了Android触摸事件的主流程 Android事件分发机制(一),本篇文章将从源码分析下事件分发的大致流程。好了马上进入主题。
Android的事件分发是从Activity的dispatchTouchEvent方法开始的,首先看看这个方法的实现。
Activity.dispatchTouchEvent
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}
Activity的dispatchTouchEvent方法里面通过getWindow方法拿到当前Activity的Window对象然后调用了它的superDispatchTouchEvent方法。
Window.superDispatchTouchEvent
public abstract boolean superDispatchTouchEvent(MotionEvent event);
Window类的superDispatchTouchEvent是一个抽象方法
Window
/**
* <p>The only existing implementation of this abstract class is
* android.view.PhoneWindow, which you should instantiate when needing a
* Window.
*/
public abstract class Window
是Window是一个抽象类,它的唯一实现类是android.view.PhoneWindow。我们看下PhoneWindow的superDispatchTouchEvent是如何实现的。
PhoneWindow.superDispatchTouchEvent
public boolean superDispatchTouchEvent(MotionEvent event) {
return mDecor.superDispatchTouchEvent(event);
}
PhoneWindow的superDispatchTouchEvent方法通过调用mDecor对象的superDispatchTouchEvent方法,继续把事件分下去了。mDecor是DecorView的对象,DecorView是FrameLayout的子类。
DecorView.superDispatchTouchEvent
public boolean superDispatchTouchEvent(MotionEvent event) {
return super.dispatchTouchEvent(event);
}
DecorView的superDispatchTouchEvent直接调了父类的dispatchTouchEvent方法,但DevorView的父类FrameLayout没有重写dispatchTouchEvent,所以事件的分发就传到了ViewGroup了。ViewGroup的dispatchTouchEvent方法有接近两百行的代码。我们删减部分代码只关注事件分发的主流程。
ViewGroup.dispatchTouchEvent
public boolean dispatchTouchEvent(MotionEvent ev) {
boolean handled = false;
if (onFilterTouchEventForSecurity(ev)) {
final int action = ev.getAction();
final int actionMasked = action & MotionEvent.ACTION_MASK;
// ACTIOO_DOWN事件,重置状态。由于anr或其他原因导致上次事件ACTION_CANCLE或ACTION_UP被抛弃了
if (actionMasked == MotionEvent.ACTION_DOWN) {
cancelAndClearTouchTargets(ev);
resetTouchState();
}
// 检查事件是否被拦截
final boolean intercepted;
//当前事件是ACTION_DOWN或者已经有子view处理,进入if语句块里面判断ViewGroup是否拦截
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;//判断是否允许ViewGroup拦截触摸事件
if (!disallowIntercept) {//disallowIntercept等于false表示允许拦截
//onInterceptTouchEvent默认返回false
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action); // restore action in case it was changed
} else {
intercepted = false;
}
} else {
//如果当前事件没有处理它的子view,也不是ACTION_DOWN事件,则父类拦截
//也就是这里说明说如果一次view不处理ACTION_DOWN事件,它就不会后续事件的原因
intercepted = true;
}
// 检查事件是否被取消
final boolean canceled = resetCancelNextUpFlag(this)
|| actionMasked == MotionEvent.ACTION_CANCEL;
final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
TouchTarget newTouchTarget = null;
boolean alreadyDispatchedToNewTouchTarget = false;
//如果没取消和拦截进入if语句块
if (!canceled && !intercepted) {
//如果当前事件是ACTION_DOWN,或者是ACTION_POINTER_DOWN,需要遍历子view,看看是否子view处理该事件。
//ACTION_POINTER_DOWN是多点触控时,一个新的手指按压下来
if (actionMasked == MotionEvent.ACTION_DOWN
|| (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
final int actionIndex = ev.getActionIndex(); // always 0 for down
final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)
: TouchTarget.ALL_POINTER_IDS;
final int childrenCount = mChildrenCount;
if (newTouchTarget == null && childrenCount != 0) {
final float x = ev.getX(actionIndex);
final float y = ev.getY(actionIndex);
//找到一个能接受事件的子view
//按照显示在最上层到最下层的顺序去遍历
final ArrayList<View> preorderedList = buildTouchDispatchChildList();
final boolean customOrder = preorderedList == null
&& isChildrenDrawingOrderEnabled();
final View[] children = mChildren;
for (int i = childrenCount - 1; i >= 0; i--) {
final int childIndex = getAndVerifyPreorderedIndex(
childrenCount, i, customOrder);
final View child = getAndVerifyPreorderedView(
preorderedList, children, childIndex);
//canViewReceivePointerEvents方法判断子view是否visible或者子view是否在播动画或者即将播动画
//isTransformedTouchPointInView方法判断点击区域是否处于子view内
if (!canViewReceivePointerEvents(child)
|| !isTransformedTouchPointInView(x, y, child, null)) {
continue;//以上两个条件要同时满足才继续处理
}
//判断当前子view之前是否有处理过该系列事件pointer的(这里可以理解为一个手指一个pointer)
//也就是子view在多指触摸事件下,处理过其他手指的事件
//如果找到了就把当前的pointerId记录在该子viewpointerIdBits变量
newTouchTarget = getTouchTarget(child);
if (newTouchTarget != null) {
newTouchTarget.pointerIdBits |= idBitsToAssign;
break;
}
//dispatchTransformedTouchEvent方法是真正把事件分发给子view处理的
//返回值为true则代表该子view处理了事件
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
mLastTouchDownTime = ev.getDownTime();
if (preorderedList != null) {
// childIndex points into presorted list, find original index
for (int j = 0; j < childrenCount; j++) {
if (children[childIndex] == mChildren[j]) {
mLastTouchDownIndex = j;
break;
}
}
} else {
mLastTouchDownIndex = childIndex;
}
//记录一些变量
mLastTouchDownX = ev.getX();
mLastTouchDownY = ev.getY();
//记录子view是新的触摸目标,addTouchTarget方法会把子view加入TouchTarget触摸链表里面
newTouchTarget = addTouchTarget(child, idBitsToAssign);
alreadyDispatchedToNewTouchTarget = true;
break;
}
}
if (preorderedList != null) preorderedList.clear();
}
//newTouchTarget为空表示没有子view处理事件
//同时mFirstTouchTarget不为空
//则把找到最先处理了事件的view相关的TouchTarget对象当做newTouchTarget
if (newTouchTarget == null && mFirstTouchTarget != null) {
newTouchTarget = mFirstTouchTarget;
while (newTouchTarget.next != null) {
newTouchTarget = newTouchTarget.next;
}
newTouchTarget.pointerIdBits |= idBitsToAssign;
}
}
}
//分发事件到触摸目标
if (mFirstTouchTarget == null) {
// 没有子view处理时,或者拦截了事件。把当前ViewGroup作为普通的view处理事件
//dispatchTransformedTouchEvent方法第三个参数接受子view,当为false时就直接调用当前viewgroup的super.dispatchTouchEvent方法,也就是View类的dispatchTouchEvent方法
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
} else {
TouchTarget target = mFirstTouchTarget;
while (target != null) {
final TouchTarget next = target.next;
if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
handled = true;
} else {
final boolean cancelChild = resetCancelNextUpFlag(target.child)
|| intercepted;
if (dispatchTransformedTouchEvent(ev, cancelChild,
target.child, target.pointerIdBits)) {
handled = true;
}
}
target = next;
}
}
}
return handled;
}
ViewGroup的dispatchTouchEvent方法处理了事件分发的主流程。一个事件如果没有拦截也没有取消,则遍历子view,从所有子view中找到处理事件的那个子view。ViewGroup是否拦截事件是通过disallowIntercept变量和onInterceptTouchEvent方法返回值共同判断的。子view可以调用父view的requestDisallowInterceptTouchEvent方法不给父view拦截事件。注意的是requestDisallowInterceptTouchEvent会继续一层一层地调用上去顶级父view。在遍历过程中,通过点击区域是否处于子view内等条件判断,找到符合条件的子view会通过调用dispatchTransformedTouchEvent方法去处理。所以真正处理事件的是dispatchTransformedTouchEvent方法。我们进去该方法里面看看流程和逻辑
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
View child, int desiredPointerIdBits) {
final boolean handled;
if (child == null || child.hasIdentityMatrix()) {
if (child == null) {
//如果子view为空,直接调了ViewGroup的父类View的dispatchTouchEvent方法
handled = super.dispatchTouchEvent(event);
} else {
//如果子view不等于空,调用子view的dispatchTouchEvent方法
//如果子view是一个ViewGroup,则递归ViewGroup的dispatchTouchEvent方法,重复上面分析的逻辑
//如果子view是普通的view,则事件分发到View的dispatchTouchEvent方法
final float offsetX = mScrollX - child.mLeft;
final float offsetY = mScrollY - child.mTop;
event.offsetLocation(offsetX, offsetY);
handled = child.dispatchTouchEvent(event);
}
return handled;
}
}
删除了大部分dispatchTransformedTouchEvent的代码,只保留了核心流程代码。 从代码注释可以看到子view不等于空时,调用子view的dispatchTouchEvent方法 但这里要注意下,如果子view是一个ViewGroup,则递归ViewGroup的dispatchTouchEvent方法,重复上面分析的逻辑,所以分析到这里可以总结出:事件是从父view通过dispatchTouchEvent方法一层一层地分发到子view
下面我们继续追踪到View的dispatchTouchEvent方法
public boolean dispatchTouchEvent(MotionEvent event) {
boolean result = false;
if (onFilterTouchEventForSecurity(event)) {
//noinspection SimplifiableIfStatement
ListenerInfo li = mListenerInfo;
//首先判断mOnTouchListener等于等于空,并且view是否ENABLED
//满足条件就会调用OnTouchListener.onTouch方法
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
result = true;
}
//只有result为false事件才分发到onTouchEvent
if (!result && onTouchEvent(event)) {
result = true;
}
}
return result;
}
View的dispatchTouchEvent方法里面首先判断 li != null && li.mOnTouchListener != null 这个条件。mOnTouchListener对象其实就算我们通过setOnTouchListener方法设置进去的。同时onTouch方法返回true,result变量就设为true了。而一个view的onClick方法是在onTouchEvent方法里面调用的,这里也解析了onTouch方法返回true为什么onClick方法就不运行了。
下面我们看看onTouchEvent方法
public boolean onTouchEvent(MotionEvent event) {
final float x = event.getX();
final float y = event.getY();
final int viewFlags = mViewFlags;
final int action = event.getAction();
//判断view是否可点击
final boolean clickable = ((viewFlags & CLICKABLE) == CLICKABLE
|| (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
|| (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE;
//判断view是否enable
if ((viewFlags & ENABLED_MASK) == DISABLED) {
if (action == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {
setPressed(false);
}
mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
//一个disable的view依然会消费事件,只是不会有任何反馈,也就是onClick方法不会再调用了,所以直接返回
return clickable;
}
//如果View是可点击的,进入if语句块处理事件
if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {
switch (action) {
case MotionEvent.ACTION_UP:
省略代码...
if (mPerformClick == null) {
mPerformClick = new PerformClick();
}
if (!post(mPerformClick)) {
performClick();//内部调用OnClickListener的onClick方法
}
省略代码...
break;
case MotionEvent.ACTION_DOWN:
省略代码...
break;
case MotionEvent.ACTION_CANCEL:
省略代码...
break;
case MotionEvent.ACTION_MOVE:
省略代码...
break;
}
return true;
}
return false;
}
可以看到onTouchEvent方法里面的逻辑,只要一个view是可点击,都会返回true,也就是消费了事件,否则返回false不消费事件。回到View的dispatchTouchEvent方法,当onTouchEvent返回false时,View的dispatchTouchEvent方法同样返回false,dispatchTransformedTouchEvent也会返回false。我们继续回到ViewGroup的dispatchTouchEvent方法。当所有子View的dispatchTransformedTouchEvent方法同样返回false时,mFirstTouchTarget 就等于null,ViewGroup的dispatchTouchEvent方法处理事件时就会调用super.dispatchTouchEvent方法。也就是ViewGroup的父类View的dispatchTouchEvent方法。至此,我们又可以总结出:如果事件分发过程中,没有任何一个View处理事件,又会通过View的OnTouchEvent方法一层一层的传递到父view。 这恰好跟dispatchTouchEvent方法相反,形成了事件传递过程中的一个U字形流程,结合上面的事件分发流程,我们总结出如图所示ACTION_DOWN事件类型的分发流程
分析到这里。我们基本上就弄清楚了Android事件分发流程,分析过程还有很多细节没有涉及。但我们只要掌握了主流程,具体细节再开发时遇到问题可以点开代码看看,就能轻松解决。