public boolean dispatchTouchEvent(MotionEvent ev) 用来进行事件的分发。如果事件能够传递给当前View,那么此方法一定会被调用,返回结果受当前View的onTouchEvent和下级View的dispatchTouchEvent方法的影响,表示是否消耗当前事件。
public boolean onInterceptTouchEvent(MotionEvent event) 在dispatchTouchEvent方法内部调用,用来判断是否拦截某个事件,如果当前View拦截了某个事件,那么在同一事件序列当中,此方法不会再次被调用,返回结果表示是否拦截当前事件。(只有ViewGroup中才有此方法,View中没有)
public boolean onTouchEvent(MotionEvent event) 在dispatchTouchEvent方法中调用,用来处理点击事件,返回结果表示是否消耗当前事件,如果不消耗,则在同一个事件序列中,当前View无法再次接收到事件。
// Handle an initial down. // ->> 分析1 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); resetTouchState(); }
// Check for interception. finalboolean intercepted; /** * 分析2 * 可以看到会有两种情况下拦截事件:事件类型为DOWN,或者mFirstTouchTarget != null。 * 那么这个mFirstTouchTarget是什么呢? * 从后面的代码我们可以得知,当ViewGroup不拦截事件交给子元素处理的时候,mFirstTouchTarget不为null。 * 所以,也就是说当MotionEvent为UP或者MOVE的时候,都进不去这个方法,也就是不调用ViewGroup的onInterceptTouchEvent,他不拦截事件 */ if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) { finalbooleandisallowIntercept= (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; } /** * 分析1 * 事件开始时会调用resetTouchState()来清空mFirstAndClearTouchTarget */ if (actionMasked == MotionEvent.ACTION_DOWN) { cancelAndClearTouchTargets(ev); // 核心目的就是清空mFirstAndClearTouchTarget resetTouchState(); }
...//省略中间代码 // 进入if语句,判断条件为没有对事件进行拦截,同时事件没有结束。对ViewGroup的子元素进行遍历 if (!canceled && !intercepted) { ...//继续省略中间代码 // 得到所有的子View final View[] children = mChildren; // 对子View进行遍历 for (inti= childrenCount - 1; i >= 0; i--) { finalintchildIndex= getAndVerifyPreorderedIndex( childrenCount, i, customOrder); finalViewchild= 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. // 该viewgroup设置事件了指向焦点View并且焦点View在前面已经找到了 if (childWithAccessibilityFocus != null) { // 判断遍历的此View是否是焦点View,如果不是就直接下一遍循环 if (childWithAccessibilityFocus != child) { continue; } // 如果是的话就将找到的焦点view置空 // i回到到数第一个下标 // 这样做的目的是先让该焦点view尝试进行下面的普通分发操作 // 如果成功了,会在下面跳出循环。 // 如果不成功,就将记录的焦点view置空, // 从最后一个开始重新遍历,不再进入这个判断。 childWithAccessibilityFocus = null; i = childrenCount - 1; }
// 判断当前子View是否能获取焦点或者是否正在做动画 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); // ->> 分析1 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 (intj=0; j < childrenCount; j++) { if (chifcldren[childIndex] == mChildren[j]) { mLastTouchDownIndex = j; break; } } } else { mLastTouchDownIndex = childIndex; } mLastTouchDownX = ev.getX(); mLastTouchDownY = ev.getY(); // ->> 分析2 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); } ... //省略 }
// 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. finalintoldAction= event.getAction(); if (cancel || oldAction == MotionEvent.ACTION_CANCEL) { event.setAction(MotionEvent.ACTION_CANCEL); if (child == null) { handled = super.dispatchTouchEvent(event); } else { handled = child.dispatchTouchEvent(event); } event.setAction(oldAction); return handled; } ...//省略 }
// 首先判断是不是不可用,如果是,则进入if if ((viewFlags & ENABLED_MASK) == DISABLED) { if (action == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) { setPressed(false); } mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN; // A disabled view that is clickable still consumes the touch // events, it just doesn't respond to them. // ->> 注释1(见上面👆) return clickable; } // 如果View有代理会执行这个方法 if (mTouchDelegate != null) { if (mTouchDelegate.onTouchEvent(event)) { returntrue; } } // 这块我们只调出ACTION_UP来看 // 只要clickable为true(见上面注释1👆)或者TOOLTIP(可能是Android8.0新出的提示功能吧) if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) { switch (action) { case MotionEvent.ACTION_UP:
...//省略 if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) { // This is a tap, so remove the longpress check removeLongPressCallback();
// Only perform take click actions if we were in the pressed state if (!focusTaken) { // Use a Runnable and post this rather than calling // performClick directly. This lets other visual state // of the view update before click actions start. if (mPerformClick == null) { mPerformClick = newPerformClick(); } if (!post(mPerformClick)) { // ->> 分析1 performClickInternal(); } } }