在上一篇我们讲了事件分发传递的顺序是从Activity到ViewGroup最后到View,本篇我们就来讲一下事件分发在ViewGroup中的传递过程,先来看源码:
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
// Handle an initial down.
if (actionMasked == MotionEvent.ACTION_DOWN) {
// Throw away all previous state when starting a new touch gesture.
// The framework may have dropped the up or cancel event for the previous gesture
// due to an app switch, ANR, or some other state change.
cancelAndClearTouchTargets(ev);
//注释1
resetTouchState();
}
// Check for interception.
final boolean intercepted;
//注释2
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;
}
} else {
// There are no touch targets and this action is not an initial down
// so this view group continues to intercept touches.
intercepted = true;
}
//注释3
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);
// If there is a view that has accessibility focus we want it
// to get the event first and if not handled we will perform a
// normal dispatch. We may do a double iteration but this is
// safer given the timeframe.
if (childWithAccessibilityFocus != null) {
if (childWithAccessibilityFocus != child) {
continue;
}
childWithAccessibilityFocus = null;
i = childrenCount - 1;
}
if (!canViewReceivePointerEvents(child)
|| !isTransformedTouchPointInView(x, y, child, null)) {
ev.setTargetAccessibilityFocus(false);
continue;
}
newTouchTarget = getTouchTarget(child);
if (newTouchTarget != null) {
// Child is already receiving touch within its bounds.
// Give it the new pointer in addition to the ones it is handling.
newTouchTarget.pointerIdBits |= idBitsToAssign;
break;
}
resetCancelNextUpFlag(child);
//注释4
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
// Child wants to receive touch within its bounds.
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();
newTouchTarget = addTouchTarget(child, idBitsToAssign);
alreadyDispatchedToNewTouchTarget = true;
break;
}
// The accessibility focus didn't handle the event, so clear
// the flag and do a normal dispatch to all children.
ev.setTargetAccessibilityFocus(false);
}
}
注释1:if条件首先判断是否是Down事件,如果是,resetTouchState方法中会把mFirstTouchTarget的值设置为null。这是为什么呢,因为一个完整的事件是从Down开始,以Up结束,所以如果是Down,则说明是一个新的事件,需要进行初始化。
注释2:ViewGroup是否拦截事件,主要看onInterceptTouchEvent方法,默认不拦截,将事件传递给子View;如果拦截了则事件不传递给子View,调用自己的onTouchEvent方法消费事件。
注释3、4:当ViewGroup不拦截事件时,事件传递给子View,通过调用dispatchTransformedTouchEvent将事件分发到子View的dispatchTouchEvent方法。
至此完成了ViewGroup的事件分发,可以用下面的伪代码表示整个流程:
public boolean dispatchTouchEvent(MotionEvent ev){
boolean handled = false;
//调用onInterceptTouchEvent判断是否拦截
if (onInterceptTouchEvent(ev)){
//拦截则调用自身的onTouchEvent
handled = onTouchEvent(ev);
}else {
//不拦截,将事件分发给子View
child.dispatchTouchEvent(ev);
}
return handled;
}