事件分发过程由下列三个方法共同完成:
-
boolean dispatchTouchEvent (MotionEvent event)
用来进行事件的分发。如果事件可以传递给当前View,那么此方法一定会被调用。返回结果受当前View的onTouchEvent和子View的dispatchTouchEvent返回结果的影响,表示是否消耗当前事件。 -
boolean onInterceptTouchEvent (MotionEvent event)
在上述方法内部调用,用来判断是否拦截某个事件。如果当前View拦截了某个事件,那么在同一个事件序列过程中,该方法不会被再次被调用。返回结果表示是否拦截当前事件。 -
boolean onTouchEvent (MotionEvent event)
在dispatchTouchEvent方法中调用,用来处理当前事件。其中,MotionEvent.ACTION_UP时会调用performClick方法,即mOnClickListener.onClick(this)方法。返回结果表示是否消耗当前事件,如果不消耗,则在同一个事件序列过程中,当前View无法再次接收到事件。
伪代码表示事件传递规则:
ViewGroup的分发过程
public boolean dispatchTouchEvent(MotionEvent event) {
boolean consume = false;
if (onInterceptTouchEvent(event)) {
consume = super.dispatchTouchEvent(event);
} else {
consume = child.dispatchTouchEvent(event);
if (!consume) {
consume = super.dispatchTouchEvent(event);
}
}
return consume;
}
View的分发过程
public boolean dispatchTouchEvent(MotionEvent event) {
boolean consume = false;
if (mOnTouchListener.onTouch(this, event)) {
consume = true;
} else {
consume = onTouchEvent(event);
}
return consume;
}
demo效果
结论:
- 事件处理优先级比较:OnTouchListener.onTouch > onTouchEvent > OnClickListener.onClick。
- 同一个事件序列是指从手指触摸屏幕的那一刻起,到手指离开屏幕的那一刻结束,在这个过程中所产生的一系列事件。这个事件序列:down -- 若干个move -- up。
- 事件传递流程:Activity -> Window -> View。
- 当一个View拦截一个事件后,那么这个事件序列都只能由它来处理,并且它的onInterceptTouchEvent方法不会再被调用。
- 正常情况下,一个事件序列只能被一个View拦截且消耗(特殊情况:View通过onTouchEvent将需要自己处理的事件强行传递给其他View)。
- 某个View一旦开始处理事件,如果它不消耗ACTION_DOWN事件(onTouchEvent返回false),那么同一事件序列中的其他事件都不会交给它来处理,而是重新交给它的父元素处理。
- 如果View不消耗ACTION_DOWN以外的其他事件,那么点击事件会消失,此时父元素的onTouchEvent并不会被调用,并且当前View可以持续收到后续事件,最终这些点击事件交给Activity处理。
- ViewGroup默认不拦截任何事件。
- View没有onInterceptTouchEvent方法。
- View的onTouchEvent默认都会消耗事件(返回true),除非它是不可点击的。
应用:滑动冲突的解决
- 外部拦截法:重写父容器的onInterceptTouchEvent方法,判断是否需要拦截
- 内部拦截法:重写子View的dispatchTouchEvent方法,判断是否消耗掉事件