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 ArrayListpreorderedList = 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方法,一层层传递回去。