android 事件分发

710 阅读11分钟

ViewGroup与View

  ViewGroup和View事件的分发,拦截,消费的相关方法如下表:

类型 相关方法 ViewGroup View
事件分发 dispatchTouchEvent
事件拦截 onInterceptTouchEvent
事件消费 onTouchEvent

这个三个方法的返回值均是Boolean类型,通过true和false来控制事件的传递和消费流程。我们先通过实际例子来看看事件传递,消费的流程。
xml:

<?xml version="1.0" encoding="utf-8"?>
<com.aoy.touch.TouchViewGroup xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.aoy.touch.MainActivity">

     <com.aoy.touch.TouchChildView
        android:layout_width="150dp"
        android:layout_height="150dp"
        android:background="@color/colorPrimary"
        android:text="TouchChild"
        android:gravity="center"
        android:textSize="16sp"
        android:textColor="#ffffff"
        android:layout_centerInParent="true"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</com.aoy.touch.TouchViewGroup>

TouchViewGroup:

class TouchViewGroup : RelativeLayout {
    val TAG : String = "TouchStudy"

    constructor(ctx: Context):super(ctx)

    constructor(ctx: Context,attrs: AttributeSet):super(ctx,attrs)

    override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
        when(ev!!.action){
            MotionEvent.ACTION_DOWN -> {
                Log.i(TAG,"ParentView dispatchTouchEvent ACTION_DOWN")
            }
            MotionEvent.ACTION_MOVE -> {
                Log.i(TAG,"ParentView dispatchTouchEvent ACTION_MOVE")
            }
            MotionEvent.ACTION_UP,MotionEvent.ACTION_CANCEL ->{
                Log.i(TAG,"ParentView dispatchTouchEvent ACTION_CANCEL")
            }
        }
        var ret = super.dispatchTouchEvent(ev)
        Log.i(TAG,"ParentView dispatchTouchEvent return :" + ret)

        return ret
    }

    override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
        when(ev!!.action){
            MotionEvent.ACTION_DOWN -> {
                Log.i(TAG,"ParentView onInterceptTouchEvent ACTION_DOWN")
            }
            MotionEvent.ACTION_MOVE -> {
                Log.i(TAG,"ParentView onInterceptTouchEvent ACTION_MOVE")
             //   return true
            }
            MotionEvent.ACTION_UP,MotionEvent.ACTION_CANCEL ->{
                Log.i(TAG,"ParentView onInterceptTouchEvent ACTION_CANCEL")
            }
        }
        var ret = super.onInterceptTouchEvent(ev)
        Log.i(TAG,"ParentView onInterceptTouchEvent return :" + ret)
        return ret
    }

    override fun onTouchEvent(event: MotionEvent?): Boolean {
        when (event!!.action) {
            MotionEvent.ACTION_DOWN -> {
                Log.i(TAG, "ParentView onTouchEvent ACTION_DOWN")
            }
            MotionEvent.ACTION_MOVE -> {
                Log.i(TAG, "ParentView onTouchEvent ACTION_MOVE")
            }
            MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
                Log.i(TAG, "ParentView onTouchEvent ACTION_CANCEL")
            }
        }
        var ret = super.onTouchEvent(event)
        Log.i(TAG,"ParentView onTouchEvent return :" + ret)
        return super.onTouchEvent(event)
    }
}

TouchChildView:

class TouchChildView : TextView {
    val TAG : String = "TouchStudy"

    constructor(ctx: Context): super(ctx)
    constructor(ctx: Context,attrs: AttributeSet): super(ctx,attrs)

    override fun dispatchTouchEvent(event: MotionEvent?): Boolean {
        when(event!!.action){
            MotionEvent.ACTION_DOWN -> {
                Log.i(TAG,"ChildView dispatchTouchEvent ACTION_DOWN")
            }
            MotionEvent.ACTION_MOVE -> {
                Log.i(TAG,"ChildView dispatchTouchEvent ACTION_MOVE")
            }
            MotionEvent.ACTION_UP,MotionEvent.ACTION_CANCEL ->{
                Log.i(TAG,"ChildView dispatchTouchEvent ACTION_CANCEL")
            }
        }
        var ret : Boolean = super.dispatchTouchEvent(event)
        Log.i(TAG,"ChildView dispatchTouchEvent return :" + ret)
        return ret
    }

    override fun onTouchEvent(event: MotionEvent?): Boolean {
        when (event!!.action) {
            MotionEvent.ACTION_DOWN -> {
                Log.i(TAG, "ChildView onTouchEvent ACTION_DOWN")
            }
            MotionEvent.ACTION_MOVE -> {
                Log.i(TAG, "ChildView onTouchEvent ACTION_MOVE")
            }
            MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
                Log.i(TAG, "ChildView onTouchEvent ACTION_CANCEL")
            }
        }
        var ret : Boolean = super.onTouchEvent(event)
        Log.i(TAG,"ChildView onTouchEvent return :" + ret)
        return ret
    }
}

TouchViewGroup与TouchChildView主要是将接收到的事件打印出来。

效果图
   初始状态下childView的onTouchEvent()和parentView的OnTouchEvent()都不消耗ACTION_DOWN事件,同时parentView也不拦截ACTION_DOWN事件,在TouchChild范围内滑动一下,我们看下log打印:

TouchStudy: ParentView dispatchTouchEvent ACTION_DOWN
TouchStudy: ParentView onInterceptTouchEvent ACTION_DOWN
TouchStudy: ParentView onInterceptTouchEvent return :false
TouchStudy: ChildView dispatchTouchEvent ACTION_DOWN
TouchStudy: ChildView onTouchEvent ACTION_DOWN
TouchStudy: ChildView onTouchEvent return :false
TouchStudy: ChildView dispatchTouchEvent return :false
TouchStudy: ParentView onTouchEvent ACTION_DOWN
TouchStudy: ParentView onTouchEvent return :false
TouchStudy: ParentView dispatchTouchEvent return :false

其事件的分发消费流程如下图 :


  由于childView在onTouchEvent()中没有消耗down事件导致childView dispatchTouchEvent返回false,这会让ParentView不会把后续的ACTION_MOVE和ACTION_UP分发给childView。同理由于childView和parentView的onTouchEvent()都没有消耗down事件,所以parentView也没有收到后续的ACTION_DOWN和ACTION_UP事件。

   现在我们让childView的onTouchEvent()消耗ACTION_DOWN事件,但ViewGroup中不拦截ACTION_DOWN事件,改动代码如下:

  /**
   * childView onTouchEvent
   */
  override fun onTouchEvent(event: MotionEvent?): Boolean {
      when (event!!.action) {
          MotionEvent.ACTION_DOWN -> {
              Log.i(TAG, "ChildView onTouchEvent ACTION_DOWN")
              return true
          }
          MotionEvent.ACTION_MOVE -> {
              Log.i(TAG, "ChildView onTouchEvent ACTION_MOVE")
          }
          MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
              Log.i(TAG, "ChildView onTouchEvent ACTION_CANCEL")
          }
      }
      var ret : Boolean = super.onTouchEvent(event)
      Log.i(TAG,"ChildView onTouchEvent return :" + ret)
      return ret
  }

  在childView中滑动之后,其log为:

TouchStudy: ParentView dispatchTouchEvent ACTION_DOWN
TouchStudy: ParentView onInterceptTouchEvent ACTION_DOWN
TouchStudy: ParentView onInterceptTouchEvent return :false
TouchStudy: ChildView dispatchTouchEvent ACTION_DOWN
TouchStudy: ChildView onTouchEvent ACTION_DOWN
TouchStudy: ChildView onTouchEvent return :true
TouchStudy: ChildView dispatchTouchEvent return :true
TouchStudy: ParentView dispatchTouchEvent return :true
TouchStudy: ParentView dispatchTouchEvent ACTION_MOVE
TouchStudy: ParentView onInterceptTouchEvent ACTION_MOVE
TouchStudy: ParentView onInterceptTouchEvent return :false
TouchStudy: ChildView dispatchTouchEvent ACTION_MOVE
TouchStudy: ChildView onTouchEvent ACTION_MOVE
TouchStudy: ChildView onTouchEvent return :false
TouchStudy: ChildView dispatchTouchEvent return :false
TouchStudy: ParentView dispatchTouchEvent return :false
TouchStudy: ParentView dispatchTouchEvent ACTION_MOVE
TouchStudy: ParentView onInterceptTouchEvent ACTION_MOVE
TouchStudy: ParentView onInterceptTouchEvent return :false
TouchStudy: ChildView dispatchTouchEvent ACTION_MOVE
TouchStudy: ChildView onTouchEvent ACTION_MOVE
TouchStudy: ChildView onTouchEvent return :false
TouchStudy: ChildView dispatchTouchEvent return :false
TouchStudy: ParentView dispatchTouchEvent return :false
TouchStudy: ParentView dispatchTouchEvent ACTION_CANCEL
TouchStudy: ParentView onInterceptTouchEvent ACTION_CANCEL
TouchStudy: ParentView onInterceptTouchEvent return :false
TouchStudy: ChildView dispatchTouchEvent ACTION_CANCEL
TouchStudy: ChildView onTouchEvent ACTION_CANCEL
TouchStudy: ChildView onTouchEvent return :false
TouchStudy: ChildView dispatchTouchEvent return :false
TouchStudy: ParentView dispatchTouchEvent return :false

  其ACTION_DOWN事件的分发,消费流程图如下:


  其ACTION_MOVE和ACITON_UP(ACTION_CANCEL)事件的分发,消费入下图:
  通过log可以看到,ACTION_MOVE与ACTION_DOWN有很大不同,当childView不消费ACTION_DOWN事件时,childView将不会收到后续的ACTION_MOVE与ACTION_UP事件,但是如果childView消费了ACTION_DOWN事件,在parentView不拦截事件的情况下,不管childView是否消费ACTION_MOVE事件,childView 还是会收到后续的ACTION_MOVE和ACTION_UP事件。换句话说View是否消费完整的一系列事件(完整的一系列事件指的是:手指按下到抬起中间所产生的所有事件)的关键取决与是否消费了ACTION_DOWN事件

   让ViewGroup中拦截ACTION_DOWN事件,改动代码如下:

 /**
   * parentView  onInterceptTouchEvent
   */
  override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
      when(ev!!.action){
          MotionEvent.ACTION_DOWN -> {
              Log.i(TAG,"ParentView onInterceptTouchEvent ACTION_DOWN")
              return true;
          }
          MotionEvent.ACTION_MOVE -> {
              Log.i(TAG,"ParentView onInterceptTouchEvent ACTION_MOVE")
           //   return true
          }
          MotionEvent.ACTION_UP,MotionEvent.ACTION_CANCEL ->{
              Log.i(TAG,"ParentView onInterceptTouchEvent ACTION_CANCEL")
          }
      }
      var ret = super.onInterceptTouchEvent(ev)
      Log.i(TAG,"ParentView onInterceptTouchEvent return :" + ret)
      return ret
  }
  
  /**
   * childView onTouchEvent
   */
  override fun onTouchEvent(event: MotionEvent?): Boolean {
      when (event!!.action) {
          MotionEvent.ACTION_DOWN -> {
              Log.i(TAG, "ChildView onTouchEvent ACTION_DOWN")
              Log.i(TAG,"ChildView onTouchEvent return :" + true)
              return true
          }
          MotionEvent.ACTION_MOVE -> {
              Log.i(TAG, "ChildView onTouchEvent ACTION_MOVE")
          }
          MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
              Log.i(TAG, "ChildView onTouchEvent ACTION_CANCEL")
          }
      }
      var ret : Boolean = super.onTouchEvent(event)
      Log.i(TAG,"ChildView onTouchEvent return :" + ret)
      return ret
  }

  这种情景下的运行log如下:

TouchStudy: ParentView dispatchTouchEvent ACTION_DOWN
TouchStudy: ParentView onInterceptTouchEvent ACTION_DOWN
TouchStudy: ParentView onTouchEvent ACTION_DOWN
TouchStudy: ParentView onTouchEvent return :false
TouchStudy: ParentView dispatchTouchEvent return :false

ACTION_DOWN事件的分发消费流程图:


这种情况下,parentView不会将任何事件分发给childView,同时parentView也没有消费ACTION_DOWN事件,导致parentView不会收到ACTION_MOVE,ACTION_UP等事件。

   让childView消耗ACTION_DOWN事件,同时让ViewGroup中拦截ACTION_MOVE事件,改动代码如下:

 /**
   * parentView  onInterceptTouchEvent
   */
  override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
      when(ev!!.action){
          MotionEvent.ACTION_DOWN -> {
              Log.i(TAG,"ParentView onInterceptTouchEvent ACTION_DOWN")
          }
          MotionEvent.ACTION_MOVE -> {
              Log.i(TAG,"ParentView onInterceptTouchEvent ACTION_MOVE")
              return true
          }
          MotionEvent.ACTION_UP,MotionEvent.ACTION_CANCEL ->{
              Log.i(TAG,"ParentView onInterceptTouchEvent ACTION_CANCEL")
          }
      }
      var ret = super.onInterceptTouchEvent(ev)
      Log.i(TAG,"ParentView onInterceptTouchEvent return :" + ret)
      return ret
  }
  
  /**
   * childView onTouchEvent
   */
  override fun onTouchEvent(event: MotionEvent?): Boolean {
      when (event!!.action) {
          MotionEvent.ACTION_DOWN -> {
              Log.i(TAG, "ChildView onTouchEvent ACTION_DOWN")
              Log.i(TAG,"ChildView onTouchEvent return :" + true)
              return true
          }
          MotionEvent.ACTION_MOVE -> {
              Log.i(TAG, "ChildView onTouchEvent ACTION_MOVE")
          }
          MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
              Log.i(TAG, "ChildView onTouchEvent ACTION_CANCEL")
          }
      }
      var ret : Boolean = super.onTouchEvent(event)
      Log.i(TAG,"ChildView onTouchEvent return :" + ret)
      return ret
  }

其运行log如下:

TouchStudy: ParentView dispatchTouchEvent ACTION_DOWN
TouchStudy: ParentView onInterceptTouchEvent ACTION_DOWN
TouchStudy: ParentView onInterceptTouchEvent return :false
TouchStudy: ChildView dispatchTouchEvent ACTION_DOWN
TouchStudy: ChildView onTouchEvent ACTION_DOWN
TouchStudy: ChildView onTouchEvent return :true
TouchStudy: ChildView dispatchTouchEvent return :true
TouchStudy: ParentView dispatchTouchEvent return :true
TouchStudy: ParentView dispatchTouchEvent ACTION_MOVE
TouchStudy: ParentView onInterceptTouchEvent ACTION_MOVE
TouchStudy: ChildView dispatchTouchEvent ACTION_CANCEL
TouchStudy: ChildView onTouchEvent ACTION_CANCEL
TouchStudy: ChildView onTouchEvent return :false
TouchStudy: ChildView dispatchTouchEvent return :false
TouchStudy: ParentView dispatchTouchEvent return :false
TouchStudy: ParentView dispatchTouchEvent ACTION_MOVE
TouchStudy: ParentView onTouchEvent ACTION_MOVE
TouchStudy: ParentView dispatchTouchEvent ACTION_CANCEL
TouchStudy: ParentView onTouchEvent ACTION_CANCEL
TouchStudy: ParentView onTouchEvent return :false
TouchStudy: ParentView dispatchTouchEvent return :false

ACTION_DOWN事件的分发消费过程图:

ACTION_MOVE的分发消费过程图:

  从③和④两种情况可以看出当parentView拦截ACTION_DOWN事件后,childView将不会收到ACTION_DOWN事和后续的ACTION_MOVE与ACTION_UP事件,而parentView能否收到后续的事件,取决于parentView的onTouchEvent是否消费ACTION_DOWN事件。当childView消费了ACTION_DOWN事件后,如果parentView拦截ACTION_MOVE事件,则后续的一系列事件都将交由parentView的ontouchEvent()来处理,不会再走onInterceptionEvent()方法,childView将不会收到后续的任何事件。

  结论:
  (1)不管是GroupView还是View,消费事件指的是消费ACTION_DOWN事件,只有消费了ACTION_DOWN事件,才有可能接收分发后续的ACTION_MOVE和ACTION_UP等事件。

  (2)对于View而言,消耗ACTION_DOWN事件只有一种途径:在onTouchEvent()中接受到ACTION_DOWN时返回true。  而GroupVie消耗ACTION_DOWN事件有两种途径:①其child view消费ACTION_DOWN;②其自身消耗ACTION_DOWN事件,即在其自身的onTouchEvent()中接受到ACTION_DOWN事件时返回true,这两种消耗ACTION_DOWN事件的关系为:parentView会先view是否消耗ACTION_DOWN分发给child view,让child view决定是否消耗ACTION_DOWN,只有当child view不消耗时,才将ACTION_DONW事件传递给其本身的onTouchEvent()方法让其判断是否消费ACTION_DOWN方法。

  (3)当GroupView拦截某一事件时,GroupView 不会将这一事件和后续的所有事件分发给child view,拦截的这一事件将会交给GroupView的onTouchEvent()处理。对于后续GroupView接收到的所有事件不会走拦截过程,而是直接交由GroupView的onTouchEvent()处理。

源码分析

  以下所有的源码都是基于API26(android 8.0)

View 的dispatchTouchEvent源码:

 public boolean dispatchTouchEvent(MotionEvent event) {
       ...

        if (onFilterTouchEventForSecurity(event)) {
        
        //关注点一:当View为可点击并且在拖动scrollBar时直接消费事件
            if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
                result = true;
            }
            
        //关注点二:当View设置为可点击并且并且设置OnTouchListener处理时,直接消费事件,并将所有的一系列事件交由OnTouchListener处理
            ListenerInfo li = mListenerInfo;
            if (li != null && li.mOnTouchListener != null
                    && (mViewFlags & ENABLED_MASK) == ENABLED
                    && li.mOnTouchListener.onTouch(this, event)) {
                result = true;
            }
        //关注点三:如果不是拖动scrollBar并且没有设置OnTouchListener则事件交由onTouchEvent()处理
            if (!result && onTouchEvent(event)) {
                result = true;
            }
        }

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

      
        if (actionMasked == MotionEvent.ACTION_UP ||
                actionMasked == MotionEvent.ACTION_CANCEL ||
                (actionMasked == MotionEvent.ACTION_DOWN && !result)) {
            stopNestedScroll();
        }

        return result;
    }

  从“关注点二”和“关注点三”中可以看出如果View设置了OnTouchListener则view直接消费事件,并将所有的事件都交由OnTouchListener处理,就onTouchEvent()什么事了,如果没有设置OnTouchListener,则是否消费事件由onTouchEvent()决定,下面看看View的onTouchEvent()源码。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();

        final boolean clickable = ((viewFlags & CLICKABLE) == CLICKABLE
                || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
                || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE;

       ...

        if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {
            switch (action) {
                case MotionEvent.ACTION_UP:
                    ...
                    break;

                case MotionEvent.ACTION_DOWN:
                   ...
                    break;

                case MotionEvent.ACTION_CANCEL:
                   ...

                case MotionEvent.ACTION_MOVE:
                   ...
                    break;
            }

            return true;
        }

        return false;
    }

  View的onTouchEvent()比较好理解,如果View设置为可点击或设置OnLongClickListener,OnClickListenerView就消费了事件。switch里的代码是具体处理长按监听和点击事件等事情。

  GroupView的dispatchTouchEvent()就比较复杂了,下面通过关键源码来看看GroupView是如何分发消费事件的。

 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) {
               //关注点一:接收到ACTION_DOWN事件时,将mFirstTouchTarget设置为 null
                cancelAndClearTouchTargets(ev);
                resetTouchState();
            }

            final boolean intercepted;
            //关注点二:当处理ACTION_DOWN或mFirstTouchTarget不为空是走拦截流程
            if (actionMasked == MotionEvent.ACTION_DOWN
                    || mFirstTouchTarget不为空是走拦截流程 != null) {
                /*关注点三:是否不允许拦截事件,当child View调用
                parent.requestDisallowInterceptTouchEvent()时,disallowIntercept = true*/
                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);
            }

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

            final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
            TouchTarget newTouchTarget = null;
            boolean alreadyDispatchedToNewTouchTarget = false;
          
            if (!canceled && !intercepted) {
                ...
                /*关注点四:只有当事件为ACTION_DOWN或者为多点触控的ACTION_POINTER_DOWN或者鼠标悬浮事件时,
                才会去在child view中寻找消费事件的view*/
                if (actionMasked == MotionEvent.ACTION_DOWN
                        || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
                        || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
                    final int actionIndex = ev.getActionIndex(); 
                    //识别手指的标志位
                    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);
             /*关注点五:先根据Z轴方法z坐标值大小排序,(Z轴是三维坐标系里的Z方向,cardView设置阴影是就是设置Z轴的值),
             再根据child view的绘制顺序排序*/
                        final ArrayList<View> preorderedList = buildTouchDispatchChildList();
                        final boolean customOrder = preorderedList == null
                                && isChildrenDrawingOrderEnabled();
                        final View[] children = mChildren;
                        for (int i = childrenCount - 1; i >= 0; i--) {
                            final int childIndex = getAndVerifyPreorderedIndex(
                                    childrenCount, i, customOrder);
                            final View child = getAndVerifyPreorderedView(
                                    preorderedList, children, childIndex);

                            ...
                            //关注点六:如果child view 不可见,并且事件不是发生在child view的范围内直接跳出查找下一个child view
                            if (!canViewReceivePointerEvents(child)
                                    || !isTransformedTouchPointInView(x, y, child, null)) {
                                ev.setTargetAccessibilityFocus(false);
                                continue;
                            }

                            newTouchTarget = getTouchTarget(child);
                            if (newTouchTarget != null) {
                                newTouchTarget.pointerIdBits |= idBitsToAssign;
                                break;
                            }

                            resetCancelNextUpFlag(child);
                            //关注点七:如果找到处理这一事件的child view,给mFirstTouchTarget赋值。
                            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;
                            }

                            ...
                        }
                        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;
                    }
                }
            }

            // 关注点九:如果没有找到child view去消费事件,将事件分发给自己,判断是否消费事件
            if (mFirstTouchTarget == null) {
                handled = dispatchTransformedTouchEvent(ev, canceled, null,
                        TouchTarget.ALL_POINTER_IDS);
            } else {
              
                TouchTarget predecessor = null;
                TouchTarget target = mFirstTouchTarget;
                //遍历mFirstTouchTarget这个链表
                while (target != null) {
                    final TouchTarget next = target.next;
                    if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
                        handled = true;
                    } else {
                    /* 关注点十:如果child view消耗事件,判断是否拦截事件,如果拦截事件则child view 将不会收到这次事件,而是收到
                    ACTION_CANCEL事件,而mFirstTouchTarget最终将会赋值为null;如果不拦截事件,则按正常流程将事件分发给child view */
                        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;
                }
            }

            /*关注点十一:当接受到的事件时ACTION_UP,ACTION_CANCEL事件时,将
            mFirstTouchTarget设置为null,重置FLAG_DISALLOW_INTERCEPT位*/
            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;
    }
  •   重点关注下mFirstTouchTarget这个对象,mFirstTouchTarget是一个链表结构的对象,正常情况下这个链表只有一个元素,当GroupView允许处理多点触控事件时,并且多点触控发生时mFirstTouchTarget可能会有多个元素。多点触控事件的分发消费流程与单点触控的流程相似,下面的说明都是以单点触控为例说明。
  •   ‘关注点五’注释的代码说明了ACTION_DOWN事件是按照child view绘制顺序来分发事件的,先绘制的child view会优先收到ACTION_DOWN事件。
  •   ‘关注点四’到‘关注点九’之间的代码就是ACTION_DOWN事件的分发消费过程。如果child view消费了ACTION_DOWN事件,在‘关注点八’的代码就给mFirstTouchTarget赋值,否则mFirstTouchTarget为null。
  •   ‘关注点九’说明如果child view没有消费ACTION_DOWN事件,其是绝对不会接受到后续的ACTION_MOVE,ACTION_UP等事件,换句话说view消费事件指的是是否消费ACTION_DOWN事件。至于消费了ACTION_DOWN事件之后,能否接受到后续的事件,就要看paren view是否拦截事件了。
  •   ‘关注点十’代码是mFirstTouchTarget的遍历过程,如果parentView拦截了事件,则child view会收到ACITON_CANCEL事件,同时mFirstTouchTarget会通过链表的遍历会最终赋值为null,这回导致后续的事件不会分发给child view而是执行‘关注点九’的代码。

下面是dispatchTransformedTouchEvent()的代码分析。

//当事件由GroupView自己处理则child = null
 private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
            View child, int desiredPointerIdBits) {
        final boolean handled;

        final int oldAction = event.getAction();
        //当GroupView拦截事件时cancel = true
        if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
            event.setAction(MotionEvent.ACTION_CANCEL);
            if (child == null) {
            //GroupView自己处理事件。 GroupView继承制View,super.dispatchTouchEvent就是调用View.dispatchTouchEvent
                handled = super.dispatchTouchEvent(event);
            } else {
            //将事件分发给child view处理
                handled = child.dispatchTouchEvent(event);
            }
            event.setAction(oldAction);
            return handled;
        }

        // 获取此次事件的所有手指标识位|运算值{@link MotionEvent#getPointerIdBits()}
        final int oldPointerIdBits = event.getPointerIdBits();
        final int newPointerIdBits = oldPointerIdBits & desiredPointerIdBits;

       //如果此次事件不合法,不处理
        if (newPointerIdBits == 0) {
            return false;
        }

    
        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);
        }

        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;
    }

  事件从activity到child view的完整分发消费图: