美文网首页android事件分发
2.View的事件分发机制(p143 第四点)

2.View的事件分发机制(p143 第四点)

作者: 真胖大海 | 来源:发表于2017-10-07 16:33 被阅读17次

    这是学习《android开发艺术探究》的笔记 如果有什么不对的地方,欢迎指出。

    1.验证P143第四点

    (4) 某个View一旦开始处理事件(即:onTouchEvent接受到事件),如果它不消耗掉ACTION_DOWN事件(onTouchEvent返回false),那么同一事件序列中的其他事件都不会被交给它处理,并且事件将重新提交,由父元素的onTouchEvent处理(父元素将处理ACIONT_DOWN,ACTION_MOVE,ACTION_UP)。

    情景 1. onInterceptTouchEvent始终拦截事件,onTouchEvent不消耗ACTION_DOWN事件

    public class MyViewGroup extends FrameLayout {
    
     @Override
     public boolean onInterceptTouchEvent(MotionEvent ev) {
         int action = ev.getAction();
         String ActionStr = Util.getActionStr(action);
         boolean consume;
         consume = true;
         Log.i(TAG, "onInterceptTouchEvent"+(consume?"拦截":"不拦截") + ActionStr);
         return consume;
     }
    
     @Override
     public boolean onTouchEvent(MotionEvent event) {
         int action = event.getAction();
         String ActionStr = Util.getActionStr(action);
         boolean consume;
         if (action == MotionEvent.ACTION_DOWN) {
             consume = false;
         } else {
             consume = true;
         }
         Log.i(TAG, "dispatchTouchEvent"+(consume?"消耗":"不消耗") + ActionStr);
         return consume;
     }
    }
    

    结果:接受不到后续事件

    onInterceptTouchEvent拦截ACTION_DOWN
    onTouchEvent不消耗ACTION_DOWN
    
    

    情景2.特例 onInterceptTouchEvent不拦截ACTION_DOWN事件,拦截其他事件,onTouchEvent不消耗事件(因为onTouchEvent没有对ACTOIN_DOWN事件返回false,所以onInterceptTouchEvent仍能接受到其他事件)

    public class MyViewGroup extends FrameLayout {
    
      @Override
      public boolean onInterceptTouchEvent(MotionEvent ev) {
          int action = ev.getAction();
          String ActionStr = Util.getActionStr(action);
          boolean consume;
          if (action == MotionEvent.ACTION_DOWN) {
              consume = false;
          } else {
              consume = true;
          }
          Log.i(TAG, "onInterceptTouchEvent"+(consume?"拦截":"不拦截") + ActionStr);
          return consume;
      }
    
     @Override
      public boolean onTouchEvent(MotionEvent event) {
          int action = event.getAction();
          String ActionStr = Util.getActionStr(action);
          boolean consume;
          consume = false;
          Log.i(TAG, "dispatchTouchEvent"+(consume?"消耗":"不消耗") + ActionStr);
          return consume;
      }
      .....
    }
    

    结果:因为onTouchEvent没有对ACTOIN_DOWN事件返回false,所以onTouchEvent仍能接受到其他事件(同时说明了第三点,如果事件被拦截,则同一事件序列都由onTouchEvent处理)

    onInterceptTouchEvent不拦截ACTION_DOWN
    onInterceptTouchEvent拦截ACTION_MOVE
    onTouchEvent不消耗ACTION_MOVE
    onTouchEvent不消耗ACTION_MOVE
    onTouchEvent不消耗ACTION_MOVE
    onTouchEvent不消耗ACTION_MOVE
    onTouchEvent不消耗ACTION_MOVE
    onTouchEvent不消耗ACTION_UP
    
    

    2.源码分析

     if (!canceled && !intercepted) {
       for(遍历child){
        if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
                                    newTouchTarget = addTouchTarget(child, idBitsToAssign);
                                    break;
        }
       }
     }
    
      if (mFirstTouchTarget == null) {
        // No touch targets so treat this as an ordinary view.
        handled = dispatchTransformedTouchEvent(ev, canceled, null,
                            TouchTarget.ALL_POINTER_IDS);
     } 
      private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
                View child, int desiredPointerIdBits) {
                if (child == null) {
                    handled = super.dispatchTouchEvent(event);
                } else {
                    handled = child.dispatchTouchEvent(event);
                }
       }
     
     
       /**
         * Adds a touch target for specified child to the beginning of the list.
         * Assumes the target child is not already present.
         */
       private TouchTarget addTouchTarget(@NonNull View child, int pointerIdBits) {
            final TouchTarget target = TouchTarget.obtain(child, pointerIdBits);
            target.next = mFirstTouchTarget;
            mFirstTouchTarget = target;
            return target;
        }
    

    2.1 关于源码的几点解说

    1. dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
      View child, int desiredPointerIdBits)

    • chile==null,ViewGroup的onTouchEvent处理
    • chile!=null,Child的onTouchEvent处理
    1. dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)返回true表示child消耗掉了事件,否则没有消耗。
    2. addTouchTarget会对mFirstTouchTarget进行赋值,使mFirstTouchTarget!=null。
    3. mFirstTouchTarget==null,有两种情况,一种是事件被拦截了,另一种是事件被拦截但是没有child消耗事件。

    2.2 源码逻辑

    如果child不消耗掉ACTION_DOWN事件,即dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)返回false,
    则mFirstTouchTarget==null,
    那么会导致

    1. dispatchTransformedTouchEvent(ev, canceled, null,TouchTarget.ALL_POINTER_IDS)会被调用,ViewGroup去处理ACTION_DOWN事件
    2. 在处理后续事件(MOVE,UP)时,actionMasked == MotionEvent.ACTION_DOWN
      || mFirstTouchTarget != null不成立,ViewGrop直接拦截事件,dispatchTransformedTouchEvent(ev, canceled, null,TouchTarget.ALL_POINTER_IDS)被调用。

    所以文中才会说

    某个View一旦开始处理事件(即:onTouchEvent接受到事件),如果它不消耗掉ACTION_DOWN事件(onTouchEvent返回false),那么同一事件序列中的其他事件都不会被交给它处理,并且事件将重新提交,由父元素的onTouchEvent处理(父元素将处理ACIONT_DOWN,ACTION_MOVE,ACTION_UP)。

    相关文章

      网友评论

        本文标题:2.View的事件分发机制(p143 第四点)

        本文链接:https://www.haomeiwen.com/subject/keijyxtx.html