美文网首页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