博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
android 事件分发
阅读量:5808 次
发布时间:2019-06-18

本文共 27832 字,大约阅读时间需要 92 分钟。

ViewGroup与View

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

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

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

xml:

复制代码

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_DOWNTouchStudy: ParentView onInterceptTouchEvent ACTION_DOWNTouchStudy: ParentView onInterceptTouchEvent return :falseTouchStudy: ChildView dispatchTouchEvent ACTION_DOWNTouchStudy: ChildView onTouchEvent ACTION_DOWNTouchStudy: ChildView onTouchEvent return :falseTouchStudy: ChildView dispatchTouchEvent return :falseTouchStudy: ParentView onTouchEvent ACTION_DOWNTouchStudy: ParentView onTouchEvent return :falseTouchStudy: 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_DOWNTouchStudy: ParentView onInterceptTouchEvent ACTION_DOWNTouchStudy: ParentView onInterceptTouchEvent return :falseTouchStudy: ChildView dispatchTouchEvent ACTION_DOWNTouchStudy: ChildView onTouchEvent ACTION_DOWNTouchStudy: ChildView onTouchEvent return :trueTouchStudy: ChildView dispatchTouchEvent return :trueTouchStudy: ParentView dispatchTouchEvent return :trueTouchStudy: ParentView dispatchTouchEvent ACTION_MOVETouchStudy: ParentView onInterceptTouchEvent ACTION_MOVETouchStudy: ParentView onInterceptTouchEvent return :falseTouchStudy: ChildView dispatchTouchEvent ACTION_MOVETouchStudy: ChildView onTouchEvent ACTION_MOVETouchStudy: ChildView onTouchEvent return :falseTouchStudy: ChildView dispatchTouchEvent return :falseTouchStudy: ParentView dispatchTouchEvent return :falseTouchStudy: ParentView dispatchTouchEvent ACTION_MOVETouchStudy: ParentView onInterceptTouchEvent ACTION_MOVETouchStudy: ParentView onInterceptTouchEvent return :falseTouchStudy: ChildView dispatchTouchEvent ACTION_MOVETouchStudy: ChildView onTouchEvent ACTION_MOVETouchStudy: ChildView onTouchEvent return :falseTouchStudy: ChildView dispatchTouchEvent return :falseTouchStudy: ParentView dispatchTouchEvent return :falseTouchStudy: ParentView dispatchTouchEvent ACTION_CANCELTouchStudy: ParentView onInterceptTouchEvent ACTION_CANCELTouchStudy: ParentView onInterceptTouchEvent return :falseTouchStudy: ChildView dispatchTouchEvent ACTION_CANCELTouchStudy: ChildView onTouchEvent ACTION_CANCELTouchStudy: ChildView onTouchEvent return :falseTouchStudy: ChildView dispatchTouchEvent return :falseTouchStudy: 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_DOWNTouchStudy: ParentView onInterceptTouchEvent ACTION_DOWNTouchStudy: ParentView onTouchEvent ACTION_DOWNTouchStudy: ParentView onTouchEvent return :falseTouchStudy: 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_DOWNTouchStudy: ParentView onInterceptTouchEvent ACTION_DOWNTouchStudy: ParentView onInterceptTouchEvent return :falseTouchStudy: ChildView dispatchTouchEvent ACTION_DOWNTouchStudy: ChildView onTouchEvent ACTION_DOWNTouchStudy: ChildView onTouchEvent return :trueTouchStudy: ChildView dispatchTouchEvent return :trueTouchStudy: ParentView dispatchTouchEvent return :trueTouchStudy: ParentView dispatchTouchEvent ACTION_MOVETouchStudy: ParentView onInterceptTouchEvent ACTION_MOVETouchStudy: ChildView dispatchTouchEvent ACTION_CANCELTouchStudy: ChildView onTouchEvent ACTION_CANCELTouchStudy: ChildView onTouchEvent return :falseTouchStudy: ChildView dispatchTouchEvent return :falseTouchStudy: ParentView dispatchTouchEvent return :falseTouchStudy: ParentView dispatchTouchEvent ACTION_MOVETouchStudy: ParentView onTouchEvent ACTION_MOVETouchStudy: ParentView dispatchTouchEvent ACTION_CANCELTouchStudy: ParentView onTouchEvent ACTION_CANCELTouchStudy: ParentView onTouchEvent return :falseTouchStudy: 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
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的完整分发消费图:

转载地址:http://ooybx.baihongyu.com/

你可能感兴趣的文章
Using RequireJS in AngularJS Applications
查看>>
hdu 2444(二分图最大匹配)
查看>>
shell编程笔记六:实现ll命令
查看>>
【SAP HANA】关于SAP HANA中带层次结构的计算视图Cacultation View创建、激活状况下在系统中生成对象的研究...
查看>>
[nodejs] nodejs开发个人博客(五)分配数据
查看>>
《Linux内核修炼之道》 之 高效学习Linux内核
查看>>
Java数据持久层框架 MyBatis之API学习九(SQL语句构建器详解)
查看>>
30分钟Git命令“从入门到放弃”
查看>>
nginx : TCP代理和负载均衡的stream模块
查看>>
MYSQL数据库间同步数据
查看>>
DevOps 前世今生 | mPaaS 线上直播 CodeHub #1 回顾
查看>>
iOS 解决UITabelView刷新闪动
查看>>
让前端小姐姐愉快地开发表单
查看>>
Dubbo笔记(四)
查看>>
Web前端JQuery入门实战案例
查看>>
java B2B2C Springboot电子商城系统- SSO单点登录之OAuth2.0 登出流程(3)
查看>>
12月26日云栖精选夜读:CDN新品发布:阿里云SCDN安全加速开放公测
查看>>
USB 通信原理
查看>>
7zZip zip RAR iOS
查看>>
date命令的详细用法!
查看>>