View的事件体系之总结

View (不包含ViewGroup)关于事件消费的优先级

onTouchListener -> onTouchDelegate -> onTouchEvent -> onClickListener

相关总结

  1. 同一个事件序列:down move... up

  2. 正常下,同一个事件序列只能被一个View拦截且消费;特殊下,可以通过onTouchEvent return false,强制传递给父View;

  3. ViewGroup一旦拦截事件,那么这个事件序列都只能由它来处理,onInterceptTouchEvent 并不会再次被调用;因为onInterceptTouchEvent调用的前提是 actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null,当ViewGroup拦截事件时,mFirstTouchTarget == null,就跳过询问是否拦截直接return true;

    继续延伸:ViewGroup 未拦截 DOWN 事件,而是从 MOVE 事件开始拦截的,那么从这个开始拦截的MOVE事件之后的事件序列,也都会交给 ViewGroup 去处理。

    原因:仍然是mFirstTouchTarget。DOWN 事件被子元素消费之后,mFirstTouchTarget 会被赋值;但是当 ViewGroup 开始拦截事件后,在其分发逻辑中会将 mFirstTouchTarget 的赋值进行回退,mFirstTouchTarget 类似一个单链表的结构,正常下仅由消费DOWN事件的子元素赋值一次的话,其 next 为 null,那么当 ViewGroup 决定拦截后,mFirstTouchTarget 会被 next 也就是 null 赋值,如此也就回到最初的逻辑,即 mFirstTouchTarget == null ViewGroup就不会进行向下分发事件。

  4. View一旦开始拦截处理事件,如果它不消费ACTION_DOWN事件(即:onTouchEvent return false),那么事件序列的其他事件都不会再交给它处理了,并且当前事件以及剩余的事件序列都会交给父View处理,即父View的onTouchEvent会被调用。原因同3,如果不消费DOWN事件,在ViewGroup的dispatch中就不会走给 mFirstTouchTarget 赋值的逻辑,mFirstTouchTarget == null,就会拦截后续事件;

  5. 如果View不消费除ACTION_DOWN之外的事件,那么这个点击事件就会消失,此时父View的onTouchEvent并不会被调用,并且当前View可以持续受到后续事件;

  6. ViewGroup默认不拦截任何事件;

  7. View没有onInterceptTouchEvent方法,一旦事件传递给它,会直接调用onTouchEvent;

  8. View的onTouchEvent默认都会消费事件(return true),除非它是不可点击的(clickable 与 longClickable同时为false)。View的 longClickable 默认位false;clickable分情况不同,Button默认是true,TextView 默认是 false。

  9. View的enable属性不影响 onTouchEvent 的返回,即使是disabled状态,只要 clickable 或者 longClickable 有一个是 true ,那么 onTouchEvent的返回就是 true;

  10. onClick 发生的前提是 当前View是可点击的,并且它收到了 down 和 up 事件;

  11. 事件传递过程是由外而内的,即事件总是先传递给父View,然后再由父View传递分发给子View;通过 requestDisallowDispatchTouchEvent 方法可以在子View中干预父View的事件分发过程,但是 ACTION_DWON 事件除外。

    原因:

    • requestDisallowDispatchTouchEvent 是ViewGroup implement ViewParent的方法;

    • ViewGroup的dispatch中会首先针对 Down事件做状态重置处理;

      // Handle an initial down.
      if (actionMasked == MotionEvent.ACTION_DOWN) {
          // Throw away all previous state when starting a new touch gesture.
          // The framework may have dropped the up or cancel event for the previous gesture
          // due to an app switch, ANR, or some other state change.
          cancelAndClearTouchTargets(ev);
          resetTouchState();
      }
      
    • 另外,requestDisallowDispatchTouchEvent 方法的调用,一般是在子View的DOWN 事件中去调用,用来干预父View的事件分发,否则会错过拦截的时机;

View事件体系.png

你可能感兴趣的:(View的事件体系之总结)