Android事件分发机制详解

Posted by Don on December 21, 2019

我想不管是在开发过程中还是在面试过程中都会碰到View事件分发机制的相关问题,今天我们就由浅入深、全面刨析一下事件分发的机制,请耐心看完,如果看完后还没有理解就请你打我。

事件分发的原理

事件分发,其实就是责任链模式的一种(责任链模式分为纯责任链与不纯责任链,此处应为纯责任链模式。责任链模式的定义:避免将一个请求的发送者与接收者耦合在一起,让多个对象都有机会处理请求。将接收请求的对象连接成一条链,并且沿着这条链传递请求,直到有一个对象能够处理它为止)
在Android中,当Activity收到触摸事件之后,会一级一级地往下传,在每一级View中,它们各自都有权利去处理(也就是拦截)这个事件,如果这次的事件传到了最底层的View,也没能处理的话,就会从这个最底层的View一级一级地向上传回去,最终会调用Activity的onTouchEvent方法。

如果是在面试过程中我们仅回答上面的答案还是远远不够的,我们还需要深挖一下以下几个问题:

  1. 处理事件分发的对象有哪些?
  2. 事件是如何处理(分发)的?
处理事件分发的对象有哪些?

Activity、ViewGroup、View

事件传递的顺序为:Activity -> ViewGroup -> View

事件是如何处理(分发)的?

这里我们首先看一下事件分发中所涉及到的3个核心方法:

  • dispatchTouchEvent()
  • onTouchEvent()
  • onInterceptTouchEvent()

它们的作用如下:

下面,我们继续结合源码来看一下具体的分发流程。

事件从Activity开始分发,我们先看一下Activity的dispatchTouchEvent()源码:

    public boolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
		//只有Down事件才会调用此方法
            onUserInteraction();
        }
	
        if (getWindow().superDispatchTouchEvent(ev)) {
            return true;//true则说明事件被消费,并停止事件传递,若返回false,则将事件交给Activity的onTouchEvent()处理
        }
        return onTouchEvent(ev);//没有View可以处理,调用Activity的onTouchEvent方法
    }


/***
*每当Key,Touch,Trackball事件分发到当前Activity就会被调用。如果你想当你的Activity在运行的时候,能够得知用户正在与你的设备交互,你可以*override该方法。
*
*这个回调方法和onUserLeaveHint是为了帮助Activities智能的管理状态栏Notification;特别是为了帮助Activities在恰当的时间取消Notification。
*
*所有Activity的onUserLeaveHint 回调都会伴随着onUserInteraction。这保证当用户相关的的操作都会被通知到,例如下拉下通知栏并点击其中的条目。
*
*注意在Touch事件分发过程中,只有Touch Down 即Touch事件的开始会触发该回调,不会在move 和 up 分发时触发
*/
    public void onUserInteraction() {
    }


//处理事件的方法,若没有view处理触摸事件最后会回调此方法
    public boolean onTouchEvent(MotionEvent event) {
	//处理发生在Window边界外的触摸事件
	// 返回true:说明事件在边界外,即 消费事件
	// 返回false:未消费(默认)
        if (mWindow.shouldCloseOnTouch(this, event)) {
            finish();
            return true;
        }

        return false;
    }

从上面的dispatchTouchEvent()中可以看出,Activity首先将事件分发给了Window进行处理,接下来那我看一下Window的源码:

public abstract class Window {
	....... 此处省略无关代码

	public abstract boolean superDispatchTouchEvent(MotionEvent event);

	......
}

我们可以看出Window是个抽象类,而且源码中有说明:The only existing implementation of this abstract class is android.view.PhoneWindow(Window的唯一实现类是PhoneWindow),那我们就看一下PhoneWindow的源码:

public class PhoneWindow extends Window implements MenuBuilder.Callback {

	// This is the top-level view of the window, containing the window decor.
	//这是window的顶级view,用于window的装饰。activity中的setContentView方法就是给DecorView设置布局
	private DecorView mDecor;

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

PhoneWindow将事件转交给DecorView进行处理,那我们继续看一下DecorView的源码:

public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {

    public boolean superDispatchTouchEvent(MotionEvent event) {
    // 调用ViewGroup的dispatchTouchEvent(),FrameLayout的父类是ViewGroup
        return super.dispatchTouchEvent(event);
    }
}

DecorView调用了它父类的dispatchTouchEvent方法进行处理,跳转进去你会发现了直接跳转到了ViewGroup的dispatchTouchEvent方法,因为FrameLayout的父类就是ViewGroup。接下来我们看一下ViewGroup中的源码:
插一句,如果有面试官问“事件分发是如何从Activity传递到ViewGroup的?”我想你应该知道怎么回答了吧。

ViewGroup: 

@UiThread
public abstract class ViewGroup extends View implements ViewParent, ViewManager {
@Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        if (mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onTouchEvent(ev, 1);
        }

	//辅助功能(朗读、放大镜等辅助功能)事件判断,带有accessibility的基本都是辅助功能相关,可以不去关注
        // If the event targets the accessibility focused view and this is it, start
        // normal event dispatch. Maybe a descendant is what will handle the click.
        if (ev.isTargetAccessibilityFocus() && isAccessibilityFocusedViewOrHost()) {
            ev.setTargetAccessibilityFocus(false);
        }

        boolean handled = false;
        if (onFilterTouchEventForSecurity(ev)) {
            final int action = ev.getAction();
            final int actionMasked = action & MotionEvent.ACTION_MASK;

            // 如果为down事件,则重置所有状态。一个完整的事件序列是以DOWN开始,以UP结束,
	    //所以如果是DOWN事件,那么说明是一个新的事件序列,所以需要初始化之前的状态
            if (actionMasked == MotionEvent.ACTION_DOWN) {
                cancelAndClearTouchTargets(ev);
                resetTouchState();
            }

            // 是否拦截事件
            final boolean intercepted;
	    //如果ViewGroup里面的子元素view能够处理事件的话,那么这个mFirstTouchTarget就会指向这个子元素view
            if (actionMasked == MotionEvent.ACTION_DOWN
                    || mFirstTouchTarget != null) {
		    //判断是否不允许拦截事件,子View在非down事件中调用requestDisallowInterceptTouchEvent来设置是否允许父View拦截事件,
		    //false表示可以拦截,true表示不可以拦截
                final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
                if (!disallowIntercept) {
		//调用onInterceptTouchEvent方法判断是否需要拦截
                    intercepted = onInterceptTouchEvent(ev);
                    ev.setAction(action); // restore action in case it was changed
                } else {
                    intercepted = false;
                }
            } else {
                //没有子元素可以处理事件,并且不是新的触摸事件则默认拦截
                intercepted = true;
            }

            // If intercepted, start normal event dispatch. Also if there is already
            // a view that is handling the gesture, do normal event dispatch.
            if (intercepted || mFirstTouchTarget != null) {
                ev.setTargetAccessibilityFocus(false);
            }

            // 事件是否取消
            final boolean canceled = resetCancelNextUpFlag(this)
                    || actionMasked == MotionEvent.ACTION_CANCEL;

	     //split是表示是否可以分发给多个孩子
            // 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) {

                // If the event is targeting accessibility focus we give it to the
                // view that has accessibility focus and if it does not handle it
                // we clear the flag and dispatch the event to all children as usual.
                // We are looking up the accessibility focused host to avoid keeping
                // state since these events are very rare.
                View childWithAccessibilityFocus = ev.isTargetAccessibilityFocus()
                        ? findChildWithAccessibilityFocus() : null;

		//ACTION_POINTER_DOWN表示有非主要的手指按下(即按下之前已经有手指在屏幕上了,多指触摸事件)
                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();//将子view排序后返回
                        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;
                            }

			// 如果子View能否接受事件,或者点击事件的坐标落在View范围内,如果2个条件有一个不满足则直接continue进入下一次循环
                            if (!canViewReceivePointerEvents(child)
                                    || !isTransformedTouchPointInView(x, y, child, null)) {
                                ev.setTargetAccessibilityFocus(false);
                                continue;
                            }
			// 当前子View能接收事件,为子View创建TouchTarget
                            newTouchTarget = getTouchTarget(child);
			    //如果有子View处理即newTouchTarget 不为null,则跳出循环
                            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);
			    // 调用dispatchTransformedTouchEvent把事件分配给子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();
				 //当child处理了点击事件,那么会设置mFirstTouchTarget指向该view, 在addTouchTarget被赋值
                                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.
                 //没有子View可以处理事件或者此控件将事件拦截,因此把自身当作一个view控件(注意,自身原本是ViewGroup类型)来处理事件,进入//dispatchTransformedTouchEvent方法后你后看到child为null时,会调用View.dispatchTouchEvent()来处理事件
		 //后面会分析dispatchTransformedTouchEvent方法
                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) {
		    //这里就是区分了ACTION_DOWN事件和别的事件,因为在在上面我们知道,如果子View消耗了ACTION_DOWN事件,那么//alreadyDispatchedToNewTouchTarget和newTouchTarget已经有值了,所以就直接置handled为true并返回
                        handled = true;
                    } else {//非down事件
                        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;
                }
            }

            // 处理CANCEL和UP事件的情况
            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;
    }




//如果没有重写此方法的话,一般情况下都会返回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;
    }



    /**
     *
     * 如果child!=null则调用child.dispatchTouchEvent方法(即ViewGroup的dispatchTouchEvent方法),
     * 否则调用父类的dispatchTouchEvent方法,即View.dispatchTouchEvent方法(因为child为null,说明此控件不包含子控件或者此控件将事件拦截,因 *此把它当作view来处理,因此会走view的dispatchTouchEvent方法),
     * 因此会执行View的onTouch() ->> onTouchEvent() ->> performClick() ->> onClick(),即自己处理该事件,事件不会往下传递(具体请参考View *事件的分发机制中的View.dispatchTouchEvent()) 
     */
    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) {
                handled = super.dispatchTouchEvent(event);
            } else {
                handled = child.dispatchTouchEvent(event);
            }
            event.setAction(oldAction);
            return handled;
        }

        // Calculate the number of pointers to deliver.
        final int oldPointerIdBits = event.getPointerIdBits();
        final int newPointerIdBits = oldPointerIdBits & desiredPointerIdBits;

        // If for some reason we ended up in an inconsistent state where it looks like we
        // might produce a motion event with no pointers in it, then drop the event.
        if (newPointerIdBits == 0) {
            return false;
        }

        // If the number of pointers is the same and we don't need to perform any fancy
        // irreversible transformations, then we can reuse the motion event for this
        // dispatch as long as we are careful to revert any changes we make.
        // Otherwise we need to make a copy.
        final MotionEvent transformedEvent;
        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);
        }

        // Perform any necessary transformations and dispatch.
        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);
        }

        // Done.
        transformedEvent.recycle();
        return handled;
    }
}

上面的代码比较多,可以借助以下流程图进行梳理:

另外经过分析后我们应该也明白了为什么子View可以通过requestDisallowInterceptTouchEvent方法干预父View的事件分发过程,但ACTION_DOWN事件除外,因为Down事件会重置所有状态,会让子View设置的FLAG_DISALLOW_INTERCEPT标志位失效。

从上面我们可以看到如果没有子view处理事件的话ViewGroup会调用View.dispatchTouchEvent方法,下面我们看一下该源码:

public class View implements Drawable.Callback, KeyEvent.Callback,
        AccessibilityEventSource {
	public boolean dispatchTouchEvent(MotionEvent event) {
	//此if代码块就是判断当前事件是否能获得焦点,如果不能获得焦点或者不存在一个View,那我们就直接返回False跳出循环
		// If the event should be handled by accessibility focus first.
		if (event.isTargetAccessibilityFocus()) {
		    // We don't have focus or no virtual descendant has it, do not handle the event.
		    if (!isAccessibilityFocusedViewOrHost()) {
			return false;
		    }
		    // We have focus and got the event, then use normal event dispatch.
		    event.setTargetAccessibilityFocus(false);
		}

		boolean result = false;

		if (mInputEventConsistencyVerifier != null) {
		    mInputEventConsistencyVerifier.onTouchEvent(event, 0);
		}

		final int actionMasked = event.getActionMasked();
		if (actionMasked == MotionEvent.ACTION_DOWN) {
		    // 当我们手指按到View上时,其他的依赖滑动都要先停下
		    stopNestedScroll();
		}

		//过滤掉一些不合法的事件,比如当前的View的窗口被遮挡了。 
		if (onFilterTouchEventForSecurity(event)) {
		    if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
			result = true;
		    }
		   //ListenerInfo 是view的一个内部类 里面有各种各样的listener,例如OnClickListener,OnTouchListener等等
		    ListenerInfo li = mListenerInfo;
		    //首先判断如果监听li对象!=null, 且是否有实现OnTouchListener,当前的view状态是不是ENABLED,OnTouchListener的onTouch中返//回值是否为true
		    if (li != null && li.mOnTouchListener != null
			    && (mViewFlags & ENABLED_MASK) == ENABLED
			    && li.mOnTouchListener.onTouch(this, event)) {
			//如果满足这些条件那么返回true,这个事件就在此处理
			result = true;
		    }
		    //如果上一段判断的条件没有满足,就判断View自身的onTouchEvent方法有没有处理,没有处理最后返回false,处理了返回true;
		    //从这里也可以看出如果上面的onTouch方法返回true的话onTouchEvent方法就不会被执行
		    if (!result && onTouchEvent(event)) {
			result = true;
		    }
		}

		if (!result && mInputEventConsistencyVerifier != null) {
		    mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
		}

		// Clean up after nested scrolls if this is the end of a gesture;
		// also cancel it if we tried an ACTION_DOWN but we didn't want the rest
		// of the gesture.
		//如果这是手势的结尾,则在嵌套滚动后清理
		if (actionMasked == MotionEvent.ACTION_UP ||
			actionMasked == MotionEvent.ACTION_CANCEL ||
			(actionMasked == MotionEvent.ACTION_DOWN && !result)) {
		    stopNestedScroll();
		}

		return result;
	    }

	      /**
     * 应用安全策略过滤触摸事件,当窗口被遮蔽时事件将会被过滤,即意味着不会执行事件分发。
     *
     * @param event 即将被过滤的事件对象
     * @return 当事件允许被分发时返回 TRUE,否则为FALSE。
     *
     * @see #getFilterTouchesWhenObscured
     */
    public boolean onFilterTouchEventForSecurity(MotionEvent event) {
        //noinspection RedundantIfStatement
	    if ((mViewFlags & FILTER_TOUCHES_WHEN_OBSCURED) != 0 && (event.getFlags() & MotionEvent.FLAG_WINDOW_IS_OBSCURED) != 0) {
		    // 当 Window 被遮蔽时,则事件将会被丢弃。
		    return false;
	    }
	    return true;
    }


    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状态,且当前View是一个可点击或者是可长按的状态    则clickable返回true。表示当前事件在此消耗且不做处理
        final boolean clickable = ((viewFlags & CLICKABLE) == CLICKABLE
                || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
                || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE;

 //如果当前View状态为DISABLED
        if ((viewFlags & ENABLED_MASK) == DISABLED) {
	//如果View的状态是被按压过,且当抬起事件产生,重置View状态为未按压,刷新Drawable的状态
            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.
            return clickable;
        }
 // 如果设置了触摸代理
        if (mTouchDelegate != null) {
            if (mTouchDelegate.onTouchEvent(event)) {
 //就交给mTouchDelegate.onTouchEvent处理,如果返回true,则事件被处理了,则不会向下传递
                return true;
            }
        }

//如果当前View的状态是可点击或者是可长按的,就对事件流进行细节处理
        if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {
            switch (action) {
                case MotionEvent.ACTION_UP:
                    mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
                    if ((viewFlags & TOOLTIP) == TOOLTIP) {
                        handleTooltipUp();
                    }
		    //清除各种状态
                    if (!clickable) {
                        removeTapCallback();
                        removeLongPressCallback();
                        mInContextButtonPress = false;
                        mHasPerformedLongPress = false;
                        mIgnoreNextUpEvent = false;
                        break;
                    }
                    //prepressed指的是,如果view包裹在一个scrolling View中,可能会进行滑动处理,所以设置了一个prePress的状态
//大致是等待一定时间,然后没有被父类拦截了事件,则认为是点击到了当前的view,从而显示点击态
                    boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;
 //如果是pressed状态或者是prepressed状态,才进行处理   
                    if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
                        // take focus if we don't have it already and we should in
                        // touch mode.
  //如果设定了获取焦点,那么调用requestFocus获得焦点
                        boolean focusTaken = false;
                        if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
                            focusTaken = requestFocus();
                        }
 //在释放之前给用户显示View的prepressed的状态,状态需要改变为PRESSED,并且需要将背景变为按下的状态为了让用户感知到
                        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 && !mIgnoreNextUpEvent) {
                            // This is a tap, so remove the longpress check
  //如果不是长按的话,仅仅是一个Tap,所以移除长按的回调
                            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.
//UI子线程去执行click,为了让click事件开始的时候其他视觉发生变化不影响。
                                if (mPerformClick == null) {
                                    mPerformClick = new PerformClick();
                                }
 //如果post消息失败,直接调用处理click事件
                                if (!post(mPerformClick)) {
                                    performClick();
                                }
                            }
                        }

                        if (mUnsetPressedState == null) {
                            mUnsetPressedState = new UnsetPressedState();
                        }

                        if (prepressed) {
 //ViewConfiguration.getPressedStateDuration() 获得的是按下效果显示的时间,由PRESSED_STATE_DURATION常量指定,在2.2中为125毫秒,也就是隔了125毫秒按钮的状态重置为未点击之前的状态。目的是让用户感知到click的效果
                            postDelayed(mUnsetPressedState,
                                    ViewConfiguration.getPressedStateDuration());
                        } else if (!post(mUnsetPressedState)) {
 //如果通过post(Runnable runnable)方式调用失败,则直接调用
                            // If the post failed, unpress right now
                            mUnsetPressedState.run();
                        }
//移除Tap的回调 重置View的状态
                        removeTapCallback();
                    }
                    mIgnoreNextUpEvent = false;
                    break;

//如果是按下的手势
                case MotionEvent.ACTION_DOWN:
                    if (event.getSource() == InputDevice.SOURCE_TOUCHSCREEN) {
                        mPrivateFlags3 |= PFLAG3_FINGER_DOWN;
                    }
//在触摸事件中执行按钮相关的动作,如果返回true则表示已经消耗了down
                    mHasPerformedLongPress = false;

                    if (!clickable) {
//仅在View支持长按时执行有效,否则直接退出方法
                        checkForLongClick(0, x, y);
                        break;
                    }

//这个performButtonActionOnTouchDown(event) 一般的设备都是返回false.因为目前的实现中,它是处理如鼠标的右键的.(如果此View响应或者其父View响应右键菜单,那么就此事件就被消耗掉了.)
                    if (performButtonActionOnTouchDown(event)) {
                        break;
                    }

  // 判断当前view是否是在滚动器当中
                    // 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;
 //将view的状态变为PREPRESSED,检测是Tap还是长按事件
                        if (mPendingCheckForTap == null) {
                            mPendingCheckForTap = new CheckForTap();
                        }
//如果是在滚动器当中,在滚动器当中的话延迟返回事件,延迟时间为 ViewConfiguration.getTapTimeout()=100毫秒
//在给定的tapTimeout时间之内,用户的触摸没有移动,就当作用户是想点击,而不是滑动.
                        mPendingCheckForTap.x = event.getX();
                        mPendingCheckForTap.y = event.getY();
                        postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
                    } else {
                        // Not inside a scrolling container, so show the feedback right away
                        setPressed(true, x, y);
                        checkForLongClick(0, x, y);
                    }
                    break;

 //接收到系统发出的ACTION_CANCLE事件时,重置状态, 将所有的状态设置为最初始
                case MotionEvent.ACTION_CANCEL:
                    if (clickable) {
                        setPressed(false);
                    }
                    removeTapCallback();
                    removeLongPressCallback();
                    mInContextButtonPress = false;
                    mHasPerformedLongPress = false;
                    mIgnoreNextUpEvent = false;
                    mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
                    break;

//如果是移动的手势
                case MotionEvent.ACTION_MOVE:
                    if (clickable) {
//将实时位置传递给背景(前景)图片
                        drawableHotspotChanged(x, y);
                    }

                    // Be lenient about moving outside of buttons
// 判断当前滑动事件是否还在当前view当中,不是就执行下面的方法
                    if (!pointInView(x, y, mTouchSlop)) {
                        // Outside button
                        // Remove any future long press/tap checks
// 移除PREPRESSED状态和对应回调
                        removeTapCallback();
                        removeLongPressCallback();
                        if ((mPrivateFlags & PFLAG_PRESSED) != 0) {
 // 是PRESSED就移除长按检测,并移除PRESSED状态
                            setPressed(false);
                        }
                        mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
                    }
                    break;
            }

            return true;
        }

        return false;
    }


//如果View设置了OnClickListener,那么会回调onClick方法
    public boolean performClick() {
        final boolean result;
        final ListenerInfo li = mListenerInfo;
        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;
    }
}

此处也提供一张流程图帮助您梳理一下逻辑:

好了,至此已经全部分析完了,最后再提供一张事件分发流程图:

参考链接:
https://www.jianshu.com/p/8527dba23512

https://www.jianshu.com/p/238d1b753e64

https://www.jianshu.com/p/38015afcdb58