事件分发源码解析

本文源码基于6.0

一.Activity中的事件分发。

1.dispatchTouchEvent。

public boolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            onUserInteraction();
        }
        if (getWindow().superDispatchTouchEvent(ev)) {
            return true;
        }
        return onTouchEvent(ev);
    }

第一步:如果是Down事件,执行onUserInteraction()方法,该方法默认是空实现,我们暂且不需要管。
第二步:调用getWindow().superDispatchTouchEvent(ev)。

public Window getWindow() {
        return mWindow;
    }

Window是一个抽象类,在Activity的attach()方法中,我们可以看到这样一行代码

final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, String referrer, IVoiceInteractor voiceInteractor) {
        ......
        mWindow = new PhoneWindow(this);
        ......
    }

PhoneWindow是Window的唯一实现类,所以getWindow().superDispatchTouchEvent(ev)调用的是PhoneWindow的superDispatchTouchEvent(ev)方法。

    @Override
    public boolean superDispatchTouchEvent(MotionEvent event) {
        return mDecor.superDispatchTouchEvent(event);
    }

我们可以看到调用了mDecor.superDispatchTouchEvent(event),mDecor是DecorView 的实例,DecorView是PhoneWindow的内部类,继承FrameLayout,

      public boolean superDispatchTouchEvent(MotionEvent event) {
          return super.dispatchTouchEvent(event);
      }

DecorView继承FrameLayout,因此会调用FrameLayout的dispatchTouchEvent()方法,由于FrameLayout并没有实现dispatchTouchEvent()方法,因此最终调用的是ViewGroup中的dispatchTouchEvent()方法,这部分后面详细分析。
第三步:如果有View消费调该事件,即getWindow().superDispatchTouchEvent(ev)返回true,则返回true,如果没有View消费该事件,即Activity的根视图以及根视图的子视图都没有拦截该事件的话,即返回false,则调用Activity的onTouchEvent()方法,Activity自己处理。

public boolean onTouchEvent(MotionEvent event) {
        if (mWindow.shouldCloseOnTouch(this, event)) {
            finish();
            return true;
        }
        return false;
    }

Window.java中的shouldCloseOnTouch方法

public boolean shouldCloseOnTouch(Context context, MotionEvent event) {
        if (mCloseOnTouchOutside && event.getAction() ==     
                MotionEvent.ACTION_DOWN
                && isOutOfBounds(context, event) && peekDecorView() != null) {
            return true;
        }
        return false;
    }

mCloseOnTouchOutside是一个boolean变量,它是由Window的android:windowCloseOnTouchOutside属性值决定。
isOutOfBounds(context, event)是判断该event的坐标是否在当前的Activity之外。是的话,返回true;否则,返回false。
peekDecorView()返回当前的DecorView。
也就是说:如果设置了android:windowCloseOnTouchOutside属性为true,并且当前事件是ACTION_DOWN,而且点击发生在Activity之外,同时Activity还包含视图的话,则返回true;表示该点击事件会导致Activity的结束。

二.ViewGroup的事件分发

在上面的分析中我们说道getWindow().superDispatchTouchEvent(ev)最终会调到ViewGroup中的dispatchTouchEvent(event)方法中,省略部分代码。

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        ......
        boolean handled = false;
        // 第一步
        if (onFilterTouchEventForSecurity(ev)) {
            final int action = ev.getAction();
            final int actionMasked = action & MotionEvent.ACTION_MASK;
            //第二步
            if (actionMasked == MotionEvent.ACTION_DOWN) {
                cancelAndClearTouchTargets(ev);
                resetTouchState();
            }
            //第三步
            final boolean intercepted;
            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 {
                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;
            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 preorderedList = buildOrderedChildList();
                        final boolean customOrder = preorderedList == null
                                && isChildrenDrawingOrderEnabled();
                        final View[] children = mChildren;
                        for (int i = childrenCount - 1; i >= 0; i--) {
                            final int childIndex = customOrder
                                    ? getChildDrawingOrder(childrenCount, i) : i;
                            final View child = (preorderedList == null)
                                    ? children[childIndex] : preorderedList.get(childIndex);

                            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);
                            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);
                        }
                        if (preorderedList != null) preorderedList.clear();
                    }

                    if (newTouchTarget == null && 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;
                    }
                }
            }

            // 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);
            } else {
                // 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 && 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;
                }
            }

            // Update list of touch targets for pointer up or cancel, if needed.
            if (canceled
                    || actionMasked == MotionEvent.ACTION_UP
                    || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
                resetTouchState();
            } else if (split && actionMasked == MotionEvent.ACTION_POINTER_UP) {
                final int actionIndex = ev.getActionIndex();
                final int idBitsToRemove = 1 << ev.getPointerId(actionIndex);
                removePointersFromTouchTargets(idBitsToRemove);
            }
        }

        if (!handled && mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1);
        }
        return handled;
    }

我们一步一步来分析:
第一步:首先调用onFilterTouchEventForSecurity(ev)来判断是否要分发该事件,该方法的实现在View中。

public boolean onFilterTouchEventForSecurity(MotionEvent event) {
        //noinspection RedundantIfStatement
        if ((mViewFlags & FILTER_TOUCHES_WHEN_OBSCURED) != 0
                && (event.getFlags() & MotionEvent.FLAG_WINDOW_IS_OBSCURED) != 0) {
            // Window is obscured, drop this touch.
            return false;
        }
        return true;
    }

如果该View不是位于顶部,并且有设置属性使该View不在顶部时不响应触摸事件,则不分发该触摸事件,即返回false。 否则,则对触摸事件进行分发,即返回true。
第二步:如果是down事件,清空之前的状态。
这里有必要说一下mFirstTouchTarget,它是接受触摸事件的View所组成的单链表。

private static final class TouchTarget {
        private static final int MAX_RECYCLED = 32;
        private static final Object sRecycleLock = new Object[0];
        private static TouchTarget sRecycleBin;
        private static int sRecycledCount;

        public static final int ALL_POINTER_IDS = -1; // all ones

        // 被触摸的view
        public View child;

        // pointerIdBits是记录触摸事件的id信息(对于多指触摸而言)
        public int pointerIdBits;

        // The next target in the target list.
        public TouchTarget next;

        private TouchTarget() {
        }

        public static TouchTarget obtain(View child, int pointerIdBits) {
            final TouchTarget target;
            synchronized (sRecycleLock) {
                if (sRecycleBin == null) {
                    target = new TouchTarget();
                } else {
                    target = sRecycleBin;
                    sRecycleBin = target.next;
                     sRecycledCount--;
                    target.next = null;
                }
            }
            target.child = child;
            target.pointerIdBits = pointerIdBits;
            return target;
        }

        public void recycle() {
            synchronized (sRecycleLock) {
                if (sRecycledCount < MAX_RECYCLED) {
                    next = sRecycleBin;
                    sRecycleBin = this;
                    sRecycledCount += 1;
                } else {
                    next = null;
                }
                child = null;
            }
        }
    }
private void cancelAndClearTouchTargets(MotionEvent event) {
        if (mFirstTouchTarget != null) {
            ......
            for (TouchTarget target = mFirstTouchTarget; target != null; target = target.next) {
                resetCancelNextUpFlag(target.child);
                dispatchTransformedTouchEvent(event, true, target.child, target.pointerIdBits);
            }
            clearTouchTargets();

            if (syntheticEvent) {
                event.recycle();
            }
        }
    }
private void resetTouchState() {
        clearTouchTargets();
        resetCancelNextUpFlag(this);
        mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
        mNestedScrollAxes = SCROLL_AXIS_NONE;
    }
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
            View child, int desiredPointerIdBits) {
        final boolean handled;
        final int oldAction = event.getAction();
        //如果cancel为true或者action为ACTION_CANCEL,设置事件为cancel,并将      
        //事件分发出去
        if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
            event.setAction(MotionEvent.ACTION_CANCEL);
            //如果child为null,则调用super.dispatchTouchEvent(event),即View中的 
            //dispatchTouchEvent(event),如果不为null,则将该事件分发给子孩子。
            if (child == null) {
                handled = super.dispatchTouchEvent(event);
            } else {
                handled = child.dispatchTouchEvent(event);
            }
            event.setAction(oldAction);
            return handled;
        }

        final int oldPointerIdBits = event.getPointerIdBits();
        final int newPointerIdBits = oldPointerIdBits & desiredPointerIdBits;
        if (newPointerIdBits == 0) {
            return false;
        }
        final MotionEvent transformedEvent;
        // 如果计算得到的前后触摸事件id信息相同,则执行不需要重新计算 
        //MotionEvent,直接执行if语句块进行消费分发;
        // 否则,就重新计算MotionEvent之后,再进行消息分发。
        if (newPointerIdBits == oldPointerIdBits) {
            if (child == null || child.hasIdentityMatrix()) {
                if (child == null) {
                    handled = super.dispatchTouchEvent(event);
                } else {
                    final float offsetX = mScrollX - child.mLeft;
                    final float offsetY = mScrollY - child.mTop;
                    event.offsetLocation(offsetX, offsetY);
                    handled = child.dispatchTouchEvent(event);
                    event.offsetLocation(-offsetX, -offsetY);
                }
                return handled;
            }
            transformedEvent = MotionEvent.obtain(event);
        } else {
            transformedEvent = event.split(newPointerIdBits);
        }
        if (child == null) {
            handled = super.dispatchTouchEvent(transformedEvent);
        } else {
            final float offsetX = mScrollX - child.mLeft;
            final float offsetY = mScrollY - child.mTop;
            transformedEvent.offsetLocation(offsetX, offsetY);
            if (! child.hasIdentityMatrix()) {
                transformedEvent.transform(child.getInverseMatrix());
            }

            handled = child.dispatchTouchEvent(transformedEvent);
        }
        transformedEvent.recycle();
        return handled;
    }

清空mPrivateFlags的PFLAG_CANCEL_NEXT_UP_EVEN标记

private static boolean resetCancelNextUpFlag(View view) {
        if ((view.mPrivateFlags & PFLAG_CANCEL_NEXT_UP_EVENT) != 0) {
            view.mPrivateFlags &= ~PFLAG_CANCEL_NEXT_UP_EVENT;
            return true;
        }
        return false;
    }

清空mFirstTouchTarget链表,并设置mFirstTouchTarget为null

private void clearTouchTargets() {
        TouchTarget target = mFirstTouchTarget;
        if (target != null) {
            do {
                TouchTarget next = target.next;
                target.recycle();
                target = next;
            } while (target != null);
            mFirstTouchTarget = null;
        }
    }

总结:cancelAndClearTouchTargets作用就是遍历mFirstTouchTarget链表,清空链表中的每一个view的PFLAG_CANCEL_NEXT_UP_EVENT标记。
第三步:是否需要拦截事件
如果是down事件或者mFirstTouchTarget不为null,执行if中的代码,首先判断是否禁止ViewGroup进行事件拦截,检查FLAG_DISALLOW_INTERCEPT标记,如果调用了requestDisallowInterceptTouchEvent()标记的话,则FLAG_DISALLOW_INTERCEPT会为true。
如果disallowIntercept为true的话,则intercepted = fasle,反之调用onInterceptTouchEvent(ev)方法

   public boolean onInterceptTouchEvent(MotionEvent ev) {
        return false;
    }

第四步:检查当前事件是否取消

final boolean canceled = resetCancelNextUpFlag(this)
                    || actionMasked == MotionEvent.ACTION_CANCEL;

对于ACTION_DOWN来说,mPrivateFlags的PFLAG_CANCEL_NEXT_UP_EVENT位肯定是0;因此,canceled=false。
当前的View或ViewGroup要被从父View中detach时,PFLAG_CANCEL_NEXT_UP_EVENT就会被设为true;此时,它就不再接受触摸事件。
第五步:将事件分发给子view。

       TouchTarget newTouchTarget = null;
       boolean alreadyDispatchedToNewTouchTarget = false;
       //如果事件没有被取消并且事件没有被拦截,则将事件分发给子view
       if (!canceled && !intercepted) {
                View childWithAccessibilityFocus = ev.isTargetAccessibilityFocus()
                        ? findChildWithAccessibilityFocus() : null;
                //事件为ACTION_DOWN
                if (actionMasked == MotionEvent.ACTION_DOWN
                        || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
                        || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
                    //对于down来说始终为0.
                    final int actionIndex = ev.getActionIndex(); // always 0 for down
                    final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)
                            : TouchTarget.ALL_POINTER_IDS;

                    removePointersFromTouchTargets(idBitsToAssign);

                    final int childrenCount = mChildrenCount;
                    if (newTouchTarget == null && childrenCount != 0) {
                        final float x = ev.getX(actionIndex);
                        final float y = ev.getY(actionIndex);
                        final ArrayList preorderedList = buildOrderedChildList();
                        final boolean customOrder = preorderedList == null
                                && isChildrenDrawingOrderEnabled();
                        final View[] children = mChildren;
                        //从后向前遍历
                        for (int i = childrenCount - 1; i >= 0; i--) {
                            final int childIndex = customOrder
                                    ? getChildDrawingOrder(childrenCount, i) : i;
                            final View child = (preorderedList == null)
                                    ? children[childIndex] : preorderedList.get(childIndex);

                            if (childWithAccessibilityFocus != null) {
                                if (childWithAccessibilityFocus != child) {
                                    continue;
                                }
                                childWithAccessibilityFocus = null;
                                i = childrenCount - 1;
                            }
                            //如果该child可以接受事件(该child是VISIBLE的或者该child不是    
                           //VISIBLE的,但是位于动画状态)并且触摸左边落在child的可视范 
                           //围内,则继续执行,否则continue。
                            if (!canViewReceivePointerEvents(child)
                                    || !isTransformedTouchPointInView(x, y, child, null)) {
                                ev.setTargetAccessibilityFocus(false);
                                continue;
                            }
                            //查找child是否存在于mFirstTouchTarget链表中
                            newTouchTarget = getTouchTarget(child);
                            //如果存在跳出for循环
                            if (newTouchTarget != null) {
                                newTouchTarget.pointerIdBits |= idBitsToAssign;
                                break;
                            }
                           // 重置child的mPrivateFlags变量中的 
                   PFLAG_CANCEL_NEXT_UP_EVENT位。
                            resetCancelNextUpFlag(child);
                           //将事件分发给子view
                            if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
                                mLastTouchDownTime = ev.getDownTime();
                                if (preorderedList != null) {
                                    for (int j = 0; j < childrenCount; j++) {
                                        if (children[childIndex] == mChildren[j]) {
                                            mLastTouchDownIndex = j;
                                            break;
                                        }
                                    }
                                } else {
                                    mLastTouchDownIndex = childIndex;
                                }
                                mLastTouchDownX = ev.getX();
                                mLastTouchDownY = ev.getY();
                                //如果该child可以接受该事件,即该child拦截了该事件或者消费 
                                //了该事件并返回true,将该view添加到mFirstTouchTarget链表表头
                                newTouchTarget = addTouchTarget(child, idBitsToAssign);
                               //将alreadyDispatchedToNewTouchTarget置为true。
                                alreadyDispatchedToNewTouchTarget = true;
                                break;
                            }
                            ev.setTargetAccessibilityFocus(false);
                        }
                        if (preorderedList != null) preorderedList.clear();
                    }
                    //如果newTouchTarget == null并且mFirstTouchTarget != null,将mFirstTouchTarget中第一个不为null的节点设置给newTouchTarget
                    if (newTouchTarget == null && mFirstTouchTarget != null) {
                        newTouchTarget = mFirstTouchTarget;
                        while (newTouchTarget.next != null) {
                            newTouchTarget = newTouchTarget.next;
                        }
                        newTouchTarget.pointerIdBits |= idBitsToAssign;
                    }
                }
            }

第六步:进一步进行事件分发

            if (mFirstTouchTarget == null) {
                //如果mFirstTouchTarget == null,意味着没有子孩子可以接受该事件,调用dispatchTransformedTouchEvent方法,注意传的参 
                //数,第三个参数为null,则会调用super.dispatchTouchEvent(event),也就是调用View.dispatchTouchEvent(event),由于ViewGroup没有覆盖onTouchEvent(event),最终会调用View.onTouchEvent(event)。
                handled = dispatchTransformedTouchEvent(ev, canceled, null,
                        TouchTarget.ALL_POINTER_IDS);
            } else {
                //如果如果mFirstTouchTarget != null,说明又可以接受事件的子孩子。
                TouchTarget predecessor = null;
                TouchTarget target = mFirstTouchTarget;
                while (target != null) {
                    final TouchTarget next = target.next;
                    if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
                        //如果alreadyDispatchedToNewTouchTarget为true(说明已经分发过),并且target等于newTouchTarget,则直接返回true
                        handled = true;
                    } else {
                        //否则分发事件给子view,主要针对MOVE和UP事件
                        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;
                }
            }

总结一下:
down事件时,相当于一次事件的开始,会清空之前的标记和mFirstTouchTarget链表。
ViewGroup默认不拦截事件。
子元素可以通过调用ViewGroup的requestDisallowInterceptTouchEvent方法修改FLAG_DISALLOW_INTERCEPT标志位的值请求不要拦截该事件,但ACTION_DOWN事件除外,因为在ACTION_DOWN事件FLAG_DISALLOW_INTERCEPT标志位会被重置。
第五步中遍历子孩子并将事件分发给可以接受事件的子孩子,注意只有down事件时才会执行第五步。此时会把可以接受事件的子孩子保存到
mFirstTouchTarget链表中。
如果某个子孩子没有接受down事件,即该子孩子没有保存到
mFirstTouchTarget链表中,那么该子孩子也不会接受到move和up事件。

三.View的事件分发。

由上面的逻辑可知,最终会调用到View的dispatchTouchEvent()方法。

public boolean dispatchTouchEvent(MotionEvent event) {
        ......
        boolean result = false;
         //如果该View不是位于顶部,并且有设置属性使该View不在顶部时不响应触摸事件,则不分发该触摸事件,即返回false。 否则,则对触摸事件进行分发,即返回true。
        if (onFilterTouchEventForSecurity(event)) {
            ListenerInfo li = mListenerInfo;
            //如果li不为null、并且设置了OnTouchListener、并且View是可点击的、并且OnTouchListener.onTouch()方法返回true,则返回true。
            if (li != null && li.mOnTouchListener != null
                    && (mViewFlags & ENABLED_MASK) == ENABLED
                    && li.mOnTouchListener.onTouch(this, event)) {
                result = true;
            }
            //调用onTouchEvent(event),如果onTouchEvent(event)返回true,则返回true,否则返回false。
            if (!result && onTouchEvent(event)) {
                result = true;
            }
        }
        ......
        return result;
    }

下面是onTouchEvent()的实现,该方法会把点击和滑动区分出来,会把点击和长按事件区分出来。

public boolean onTouchEvent(MotionEvent event) {
        final float x = event.getX();
        final float y = event.getY();
        final int viewFlags = mViewFlags;
        final int action = event.getAction();
        //当view的状态为DISABLED(被禁用,调用setEnabled(false)时,View就被禁用了)时,返回它是否时可点击的(仍然会消费掉事件,只是没有效果而已)
        if ((viewFlags & ENABLED_MASK) == DISABLED) {
            if (action == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {
                setPressed(false);
            }
            return (((viewFlags & CLICKABLE) == CLICKABLE
                    || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
                    || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE);
        }
        //当我们设置了TouchDelegate监听代理时,会调用mTouchDelegate.onTouchEvent(event),类似于扩大点击范围时,mTouchDelegate默认情况下为null。
        if (mTouchDelegate != null) {
            if (mTouchDelegate.onTouchEvent(event)) {
                return true;
            }
        }
        //如果不可点击(既不能单击,也不能长按)则直接返回false
        if (((viewFlags & CLICKABLE) == CLICKABLE ||
                (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) ||
                (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE) {
            switch (action) {
                // 下面事件分开讲解
            }

            return true;
        }

        return false;
    }

case MotionEvent.ACTION_DOWN:

case MotionEvent.ACTION_DOWN:
        //处理长按事件标识
        mHasPerformedLongPress = false;

        if (performButtonActionOnTouchDown(event)) {
               break;
        }
        //判断是否正在滚动的容器中,不能把滑动当前点击.所以先判断是不是在一个可滑动的容器中
        boolean isInScrollingContainer = isInScrollingContainer();
        if (isInScrollingContainer) {
           //如果是在一个可滚动的容器中,先设置用户准备点击这么一个标志位:PFLAG_PREPRESSED,然后则发送一个延迟消息来确定用户到底是要滚动还是点击
            mPrivateFlags |= PFLAG_PREPRESSED;
            if (mPendingCheckForTap == null) {
                  mPendingCheckForTap = new CheckForTap();
            }
            mPendingCheckForTap.x = event.getX();
            mPendingCheckForTap.y = event.getY();
            //在给定的tapTimeout时间之内,用户的触摸没有移动,就当作用户是想点击,而不是滑动
            postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
         } else {
             //如果不是在一个可滚动的容器中,调用setPressed(true) 设置按下状态.,setPressed 主要是设置PFLAG_PRESSED标志位,检查长按。
             setPressed(true, x, y);
             checkForLongClick(0);
         }
         break;

检查点击还是滑动的具体做法:将 CheckForTap的实例mPendingCheckForTap添加时消息队例中,延迟执行.
如果在这tagTimeout之间用户触摸移动了,则删除此消息.否则:执行按下状态.然后检查长按,检查长按事件的思路也类似。
CheckForTap消息方法如下:

private final class CheckForTap implements Runnable {
        public float x;
        public float y;

        @Override
        public void run() {
            mPrivateFlags &= ~PFLAG_PREPRESSED;
            setPressed(true, x, y);
            checkForLongClick(ViewConfiguration.getTapTimeout());
        }
    }

case MotionEvent.ACTION_MOVE:

case MotionEvent.ACTION_MOVE:
        drawableHotspotChanged(x, y);
        //判断触摸点是否在此view中,先将上下左右增大mTouchSlop个像素,再判断。
        if (!pointInView(x, y, mTouchSlop)) {
              //如果超出view之外,将处理点击消息移除
             removeTapCallback();
             if ((mPrivateFlags & PFLAG_PRESSED) != 0) {
                  //如果是已经准备长按了,则将长按的消息移除.并将View的按下状态设置为false。
                 removeLongPressCallback();
                 setPressed(false);
             }
         }
        break;

case MotionEvent.ACTION_UP:

case MotionEvent.ACTION_UP:
       boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;
       //首先是检查 PFLAG_PREPRESSED 和PFLAG_PRESSED 这两个标志.如果其中一个为真则处理。
       //这两个标志位首先是在开始触控时(即手指按下ACTION_DOWN)时设置,PFLAG_PREPRESSED 表示在一个可滚动的容器中,要稍后才能确定是按下还是滚动,PFLAG_PRESSED 表示不是在一个可滚动的容器中,已经可以确定按下这一操作。
       if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
           boolean focusTaken = false;
           if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
              focusTaken = requestFocus();
           }

           if (prepressed) {
              setPressed(true, x, y);
           }
            //判断是否进行了长按,如果没有,则移除长按
           if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {
               removeLongPressCallback();
               //判断有没有重新请求获得焦点,如果还没有新获得焦点,说明之前已经是按下的状态了。
               if (!focusTaken) {
                  if (mPerformClick == null) {
                     mPerformClick = new PerformClick();
                  }
                  //派发执行点击操作的消息
                  if (!post(mPerformClick)) {
                     performClick();
                  }
                }
            }

            if (mUnsetPressedState == null) {
                mUnsetPressedState = new UnsetPressedState();
            }
            //派发消息来取消点击状态
            if (prepressed) {
                 postDelayed(mUnsetPressedState,
                                    ViewConfiguration.getPressedStateDuration());
             } else if (!post(mUnsetPressedState)) {
                  mUnsetPressedState.run();
             }

             removeTapCallback();
        }
        mIgnoreNextUpEvent = false;
        break;
private final class PerformClick implements Runnable {
        @Override
        public void run() {
            performClick();
        }
    }
public boolean performClick() {
        final boolean result;
        final ListenerInfo li = mListenerInfo;
        //如果li不为null并且设置了OnClickListener,则执行onClick()方法。
        if (li != null && li.mOnClickListener != null) {
            playSoundEffect(SoundEffectConstants.CLICK);
            li.mOnClickListener.onClick(this);
            result = true;
        } else {
            result = false;
        }

        sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
        return result;
    }

case MotionEvent.ACTION_CANCEL:

 //重置按钮状态及变量的值,移除点击和长按的检查
  case MotionEvent.ACTION_CANCEL:
       setPressed(false);
       removeTapCallback();
       removeLongPressCallback();
       mInContextButtonPress = false;
       mHasPerformedLongPress = false;
       mIgnoreNextUpEvent = false;
       break;

好了,事件分发的源码分析就到此结束了。

你可能感兴趣的:(事件分发源码解析)