View (不包含ViewGroup)关于事件消费的优先级
onTouchListener -> onTouchDelegate -> onTouchEvent -> onClickListener
相关总结
同一个事件序列:down move... up
正常下,同一个事件序列只能被一个View拦截且消费;特殊下,可以通过onTouchEvent return false,强制传递给父View;
-
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就不会进行向下分发事件。
View一旦开始拦截处理事件,如果它不消费ACTION_DOWN事件(即:onTouchEvent return false),那么事件序列的其他事件都不会再交给它处理了,并且当前事件以及剩余的事件序列都会交给父View处理,即父View的onTouchEvent会被调用。原因同
3
,如果不消费DOWN事件,在ViewGroup的dispatch中就不会走给 mFirstTouchTarget 赋值的逻辑,mFirstTouchTarget == null,就会拦截后续事件;如果View不消费除ACTION_DOWN之外的事件,那么这个点击事件就会消失,此时父View的onTouchEvent并不会被调用,并且当前View可以持续受到后续事件;
ViewGroup默认不拦截任何事件;
View没有onInterceptTouchEvent方法,一旦事件传递给它,会直接调用onTouchEvent;
View的onTouchEvent默认都会消费事件(return true),除非它是不可点击的(clickable 与 longClickable同时为false)。View的 longClickable 默认位false;clickable分情况不同,Button默认是true,TextView 默认是 false。
View的enable属性不影响 onTouchEvent 的返回,即使是disabled状态,只要 clickable 或者 longClickable 有一个是 true ,那么 onTouchEvent的返回就是 true;
onClick 发生的前提是 当前View是可点击的,并且它收到了 down 和 up 事件;
-
事件传递过程是由外而内的,即事件总是先传递给父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的事件分发,否则会错过拦截的时机;