view,viewGroup点击事件传递

这是view点击事件的传递流程,我们先看看 view.onTouchEvent()干了什么。

从Down事件开始

case MotionEvent.ACTION_DOWN:
        ......
        if (!clickable) {
            checkForLongClick(
                    ViewConfiguration.getLongPressTimeout(),
                    x,
                    y,
                    TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__LONG_PRESS);
            break;
        }  
        **// 当空间不可点击时,给个长按时间监听,显示tooltipText文本**
<pre><code>    // Walk up the hierarchy to determine if we're inside a scrolling container.
    boolean isInScrollingContainer = isInScrollingContainer(); 
    **//判断是不是在滑动控件内部**

    // For views inside a scrolling container, delay the pressed feedback for
    // a short period in case this is a scroll.
    if (isInScrollingContainer) {
        mPrivateFlags |= PFLAG_PREPRESSED;
        if (mPendingCheckForTap == null) {
            mPendingCheckForTap = new CheckForTap();
        }
        mPendingCheckForTap.x = event.getX();
        mPendingCheckForTap.y = event.getY();
        postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
        **//如果是在则设置预按下状态,给个100毫秒的监听,后在给个长按监听,判断用户是不是要操控控件,所以自定义viewGroup最好重写isInScrollingContainer
        //减少点击事件这100毫秒的延时**
    } else {
        // Not inside a scrolling container, so show the feedback right away
        setPressed(true, x, y);   
        checkForLongClick(
                ViewConfiguration.getLongPressTimeout(),
                x,
                y,
                TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__LONG_PRESS);
    }
    break;
     **//否者这,设置按下状态,给个长按监听**

move事件

case MotionEvent.ACTION_MOVE:
if (clickable) {
drawableHotspotChanged(x, y);
}
<strong>//绘制水波纹</strong></p>
<pre><code>    .....
    if (!pointInView(x, y, touchSlop)) {
        // Outside button
        // Remove any future long press/tap checks
        removeTapCallback();
        removeLongPressCallback();
        if ((mPrivateFlags &amp; PFLAG_PRESSED) != 0) {
            setPressed(false);
        }
        mPrivateFlags3 &amp;= ~PFLAG3_FINGER_DOWN;
    }
    **//当触点移动到控件外部时,直接取消view事件的各种状态,爷不干了**
    .......

up事件

mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
if ((viewFlags & TOOLTIP) == TOOLTIP) {
handleTooltipUp();
}
//有tooltip则延时1500毫秒取消显示tooltip
if (!clickable) {
removeTapCallback();
removeLongPressCallback();
mInContextButtonPress = false;
mHasPerformedLongPress = false;
mIgnoreNextUpEvent = false;
break;
}
//如果不可点击了,爷就不干活了,直接擦屁股</p>
<pre><code>    boolean prepressed = (mPrivateFlags &amp; PFLAG_PREPRESSED) != 0;
    if ((mPrivateFlags &amp; PFLAG_PRESSED) != 0 || prepressed) {
        // take focus if we don't have it already and we should in
        // touch mode.
        boolean focusTaken = false;
        if (isFocusable() &amp;&amp; isFocusableInTouchMode() &amp;&amp; !isFocused()) {
            focusTaken = requestFocus();
        }
        //给需要获取获取焦点的控件获取焦点

        if (prepressed) {
            // The button is being released before we actually
            // showed it as pressed.  Make it show the pressed
            // state now (before scheduling the click) to ensure
            // the user sees it.
            setPressed(true, x, y);
        }
        //如果是预按下状态,更新为按下状态

        if (!mHasPerformedLongPress &amp;&amp; !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 = new PerformClick();
                }
                if (!post(mPerformClick)) {
                    performClickInternal();
                }
            }
        }
        //如果不是长按和轻敲这执行点击事件

        if (prepressed) {
            postDelayed(mUnsetPressedState,
                    ViewConfiguration.getPressedStateDuration());
        } else if (!post(mUnsetPressedState)) {
            // If the post failed, unpress right now
            mUnsetPressedState.run();
        }
        //如果是预按下抬起的则,延时一会在设置回来未按下状态

CANCEL事件就纯擦屁股了,没必要看了

我们现在在过一遍ViewGroup的

public boolean dispatchTouchEvent(MotionEvent ev) {
if (onFilterTouchEventForSecurity(ev)) {
final int action = ev.getAction();
final int actionMasked = action & MotionEvent.ACTION_MASK;</p>
<pre><code>        // 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);
            resetTouchState();
        }
        **// 如果是down事件,做一些重置操作**

        final boolean intercepted;
        if (actionMasked == MotionEvent.ACTION_DOWN
                || mFirstTouchTarget != null) {
            final boolean disallowIntercept = (mGroupFlags &amp; FLAG_DISALLOW_INTERCEPT) != 0;
            //看看子view是不是申请了,不拦截

            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;
            **//是down,先不拦截,让子view去抢或mFirstTouchTarget是一条子view的onTouch链条,
            //如果第二次进来都没有子view调用的话,我也就不用干活了**
        }

        if (newTouchTarget == null &amp;&amp; childrenCount != 0) {
            for (int i = childrenCount - 1; i &gt;= 0; i--) {
                final int childIndex = getAndVerifyPreorderedIndex(
                        childrenCount, i, customOrder);
                final View child = getAndVerifyPreorderedView(
                        preorderedList, children, childIndex);
                if (!child.canReceivePointerEvents()
                        || !isTransformedTouchPointInView(x, y, child, null)) {
                    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);
                if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
                    **//开始递归执行dispatchTouchEvent**
                    // 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 &lt; 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);
            }
            if (preorderedList != null) preorderedList.clear();
        }

        if (newTouchTarget == null &amp;&amp; mFirstTouchTarget != null) {
            // Did not find a child to receive the event.
            // Assign the pointer to the least recently added target.
            newTouchTarget = mFirstTouchTarget;
            while (newTouchTarget.next != null) {
                newTouchTarget = newTouchTarget.next;
            }
            newTouchTarget.pointerIdBits |= idBitsToAssign;
        }
        **//更新需要dispatchTouchEvent view的链条**

        // Dispatch to touch targets.
        if (mFirstTouchTarget == null) {
            // No touch targets so treat this as an ordinary view.
            handled = dispatchTransformedTouchEvent(ev, canceled, null,
                    TouchTarget.ALL_POINTER_IDS);
            **//链条没东西,就调用自己的dispatchTouchEvent**
        } else {
            //链条有东西,就调用链条上dispatchTouchEvent
            // Dispatch to touch targets, excluding the new touch target if we already
            // dispatched to it.  Cancel touch targets if necessary.
            TouchTarget predecessor = null;
            TouchTarget target = mFirstTouchTarget;
            while (target != null) {
                final TouchTarget next = target.next;
                if (alreadyDispatchedToNewTouchTarget &amp;&amp; target == newTouchTarget) {
                    handled = true;
                } else {
                    final boolean cancelChild = resetCancelNextUpFlag(target.child)
                            || intercepted;
                    if (dispatchTransformedTouchEvent(ev, cancelChild,
                            target.child, target.pointerIdBits)) {
                        handled = true;
                    }
                    if (cancelChild) {
                        if (predecessor == null) {
                            mFirstTouchTarget = next;
                        } else {
                            predecessor.next = next;
                        }
                        target.recycle();
                        target = next;
                        continue;
                    }
                }
                predecessor = target;
                target = next;
            }
        }
        }
    }.

private TouchTarget addTouchTarget(@NonNull View child, int pointerIdBits) { final TouchTarget target = TouchTarget.obtain(child, pointerIdBits); target.next = mFirstTouchTarget; mFirstTouchTarget = target; return target; }

我只能说一句牛批