1、dispatchTouchEvent:
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
//最终返回值
boolean handled = false;
if (onFilterTouchEventForSecurity(ev)) {
final int action = ev.getAction();
final int actionMasked = action & MotionEvent.ACTION_MASK;
// Handle an initial down.
if (actionMasked == MotionEvent.ACTION_DOWN) {
//做一些重置工作
//将mFirstTouchTarget置空
cancelAndClearTouchTargets(ev);
//将FLAG_DISALLOW_INTERCEPT等状态标识还原
resetTouchState();
}
//是否要做事件拦截,如果为false,不做拦截继续分发,为true则阻止分发
final boolean intercepted;
(1)//按下并且mFirstTouchTarget不为空
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
(2)//FLAG_DISALLOW_INTERCEPT标志位
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
(3)
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;
}
if (intercepted || mFirstTouchTarget != null) {
ev.setTargetAccessibilityFocus(false);
}
// Check for cancelation.
final boolean canceled = resetCancelNextUpFlag(this)
|| actionMasked == MotionEvent.ACTION_CANCEL;
// Update list of touch targets for pointer down, if needed.
final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
TouchTarget newTouchTarget = null;
boolean alreadyDispatchedToNewTouchTarget = false;
//主要做对子View事件分发的工作
if (!canceled && !intercepted) {
View childWithAccessibilityFocus = ev.isTargetAccessibilityFocus()
? findChildWithAccessibilityFocus() : null;*/
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;
// Clean up earlier touch targets for this pointer id in case they
// have become out of sync.
removePointersFromTouchTargets(idBitsToAssign);
final int childrenCount = mChildrenCount;
if (newTouchTarget == null && childrenCount != 0) {
final float x = ev.getX(actionIndex);
final float y = ev.getY(actionIndex);
// Find a child that can receive the event.
// Scan children from front to back.
final ArrayList<View> preorderedList = buildTouchDispatchChildList();
final boolean customOrder = preorderedList == null
&& isChildrenDrawingOrderEnabled();
final View[] children = mChildren;
//对子View的集合进行遍历
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;
}
//查找到当前可以接受事件的子View,整个循环结束
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)//用来将事件传递给子View
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();
//实现了mFirstTouchTarget的赋值
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);
}
if (preorderedList != null) preorderedList.clear();
}
......
return handled;
}
复制代码
(1)mFirstTouchTarget: 如果事件由子元素处理,mFirstTouchTarget会被赋值并指向子元素,此时mFirstTouchTarget != null。如果mFirstTouchTarget == null,会导致ViewGroup的onInterceptTouchEvent方法不会再被调用,并且同一系列其他的事件默认交给ViewGroup来处理。
(2)FLAG_DISALLOW_INTERCEPT标识位: FLAG_DISALLOW_INTERCEPT标识位可以来干预ViewGroup的分发,可以在子元素中通过调用reqeustDisallowInterceptTouchEvent方法来干预父元素的事件分发过程,但是ACTION_DOWN除外。 因为ACTION_DOWN事件会重置FLAG_DISALLOW_INTERCEPT这个标志位,导致子View设置的这个标志位无效。 (3)intercepted : 这个标识量为true,则不会再调用子View的dispatchTouchEvent方法。intercepted 通过onInterceptTouchEvent来返回值进行赋值。也就是说onInterceptTouchEvent返回true,则不会进行子View的事件分发。
/**
* 子view可以通过该方法控制父View的拦截
* @param disallowIntercept
*/
@Override
public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
if (disallowIntercept == ((mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0)) {
// We're already in this state, assume our ancestors are too
return;
}
if (disallowIntercept) {
mGroupFlags |= FLAG_DISALLOW_INTERCEPT;
} else {
mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
}
// Pass it up to our parent
if (mParent != null) {
mParent.requestDisallowInterceptTouchEvent(disallowIntercept);
}
}
复制代码
(4) dispatchTransformedTouchEvent 遍历ViewGroup所有子元素,判断子元素能否接受点击事件(通过点击区域和是否播放动画来判断)。如果可以接受点击事件,则调用dispatchTransformedTouchEvent方法,内部通过child.dispatchTouchEvent(event)方法实现到子View的事件传递。
/**
* 对子View的事件分发
*/
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
View child, int desiredPointerIdBits) {
final boolean handled;
// Canceling motions is a special case. We don't need to perform any transformations
// or filtering. The important part is the action, not the contents.
final int oldAction = event.getAction();
if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
event.setAction(MotionEvent.ACTION_CANCEL);
if (child == null) {
//子view为空则调用View的dispatchTouchEvent方法
handled = super.dispatchTouchEvent(event);
} else {
//调用child的dispatchTouchEvent方法
handled = child.dispatchTouchEvent(event);
}
event.setAction(oldAction);
return handled;
}
......
}
复制代码
2、onInterceptTouchEvent:
默认返回false
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (ev.isFromSource(InputDevice.SOURCE_MOUSE)
&& ev.getAction() == MotionEvent.ACTION_DOWN
&& ev.isButtonPressed(MotionEvent.BUTTON_PRIMARY)
&& isOnScrollbarThumb(ev.getX(), ev.getY())) {
return true;
}
return false;
}
复制代码
3、onTouchEvent:
调用了View的onTouchEvent方法
4、三个方法之间的关系:
public boolean dispatchTouchEvent(MotionEvent ev) {
// 默认状态为没有消费过
boolean result = false;
// 如果没有拦截交给子View进行分发
if (!onInterceptTouchEvent(ev)) {
result = child.dispatchTouchEvent(ev);
}
// 如果事件没有被消费,询问自身onTouchEvent
if (!result) {
result = onTouchEvent(ev);
}
return result;
}
复制代码
- 如果ViewGroup不拦截,交于子View的onTouchEvent处理。
- 如果子View的onTouchEvent返回false,就会调用该ViewGroup的onTouchEvent方法,如果ViewGroup的onTouchEvent也返回false,最终会抛回Activity的onTouchEvent方法,一层层传递回去。