1、View的事件分发涉及到3个核心的方法:
1.1、第一个是dispatchtouchevent,这个方法是用来进行事件分发,如果事件能够传递给当前 view, 那么此方法一定被调用,它的返回值受当前view的ontouchevent和下级view的 dispatchevent的影响,表示是否消耗当前事件;
1.2、第二个方法是oninterceptouchevent,这个方法在 dispatchtouchevent方法内部调用,用来判断是否拦截某个事件,如果当前view拦截了某个事件 ,那么在同一事件序列中 ,此方法不会 再次被调用,返回的结果表示是否拦截当前事件;
1.3、第三个方法是ontouchevent,这个方法也是在dispatchtouchevent方法中调用,用来处理事件,返回的结果表示是否消耗当前事件,如果不消耗,同一 事件序列中 ,当前view无法再次接 受到此事件序列中的后续事件。
三个方法的联系可用一下伪代码生动描述
public boolean dispatchTouchEvent(MotionEvent event) {
boolean consume = false;
if(onInterceptTouchEvent(ev)) {
consume = onTouchEvent(ev);
} else {
consume = child.dispatchTouchEvent(ev);
}
return consume;
}
2、具体过程
具体来说,对于一个根 viewgroup来说,事件产生后,首先会传递给它,这时它的dispatchtouchevent方法会被调用,如果这个viewgroup的interceptedtouchevent方法返回为 true,就表示它要拦截当前事件,那么它的 ontouchevent方法会被调用,去处理这个事件 ;
如果这个viewgroup的oninterceptouchevent方法返回为 false,就表示它不拦截当前事件, 这时当前事件就会继续传递给它的子view,接着子view的dispatchtouchevent方法就会被调用,如此往下,直至事件最终被处理。
当一个 view开始处理某个事件,如果它设置了ontouchlistener, 那么它的ontouch方法会被调用,如果ontouch返回为true,那么view的ontouchevent方法不会被调用,如果返回为false,当前view的ontouchevent方法会被调用,如果当前view还设置了onclicklistener,那么onclick也会被随后调用。
3、事件分发的一些结论
(1)、若 ViewGroup 拦截了一个半路的事件(如MOVE),该事件将会被系统变成一个CANCEL事件 & 传递给之前处理该事件的子View;
该事件不会再传递给ViewGroup的onTouchEvent();
只有再到来的事件才会传递到ViewGroup的onTouchEvent()。
因为一旦当前事件变成取消事件,mFirstTouchTarget就会被置为null。
(2)、ViewGroup的onInterceptTouchEvent()对事件返回了false,但后续的事件(MOVE、UP)依然会传递给它的onInterceptTouchEvent() ;只有在onInterceptTouchEvent方法返回为true,此方法将不会被调用。
(3)、某个View一旦开始处理事件,如果它不消耗ACTION_DOWN事件(onTouchEvent()返回了false),那么同一事件序列中的其他事件都不会再交给它处理,并且事件将重新交由它的父元素去处理,即父元素的onTouchEvent()会被调用。
(4)、ViewGroup默认不拦截事件。onInterceptTouchEvent()方法默认返回false。
(5)、View没有onInterceptTouchEvent()方法,一旦有事件传递给它,那么它的onTouchEvent()方法就会被调用。
(6)、View的onTouchevent()默认都会消耗事件(返回true),除非它是不可点击的(clickable和longClickable同时为false)。
(7)、onClick会发生的前提是当前View是可点击的,并且它收到了down和up的事件。
(8)、当面对ACTION_DOWN事件时,ViewGroup总是会调用自己的onInterceptTouchEvent()方法来询问自己是否要拦截该事件。
以下是安卓艺术探索没说清楚的:
(9)、正常情况下,一个事件序列只能被一个View拦截并消耗。(异常情况对应第1条)
(10)某个View一旦决定拦截,那么这一个事件序列都只能由它来处理(如果事件序列能够传递给它的话,异常情况对应第一条),并且它的onInterceptTouchEvent()不会再被调用。
(2)(3)证明代码
public boolean dispatchTouchEvent(MotionEvent ev) {
..............
//mFirstTouchTarget是ViewGroup中处理事件(return true)的子View
//如果没有子View处理则mFirstTouchTarget=null,ViewGroup自己处理
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
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 {
// There are no touch targets and this action is not an initial down
// so this view group continues to intercept touches.
intercepted = true;
}
}
当事件是ACTION_DOWN或者有子View去处理这个事件时,父view的onInterceptTouchEvent方法才会去调用,(ViewGroup在两种情况下会去判断是否拦截事件(ACTION_DOWN || mFirstTouchTarget != null)),反过来说就是: 当当前事件是ACTION_MOVE和ACTION_UP事件时,如果子View没有处理down事件(mFirstTouchTarget==null),(第一种情况:是父view的onInterceptTouchEvent返回true,处理了事件;第一种情况:是子view没有消耗down事件。不管是哪种情况)
ViewGroup的onInterceptTouchEvent不会再被调用,后续事件都会默认交给父view处理;子view也无法接收到后续事件。
1,view滑动总结:https://www.jianshu.com/p/61ad263c4a0e
2,view滑动冲突
2,Android事件分发机制 详解攻略,您值得拥有:https://blog.csdn.net/carson_ho/article/details/54136311
3,事件分发机制全解析https://juejin.cn/post/6844903849723953166