事件分發(fā)過程由下列三個方法共同完成:
-
boolean dispatchTouchEvent (MotionEvent event)
用來進(jìn)行事件的分發(fā)。如果事件可以傳遞給當(dāng)前View,那么此方法一定會被調(diào)用。返回結(jié)果受當(dāng)前View的onTouchEvent和子View的dispatchTouchEvent返回結(jié)果的影響,表示是否消耗當(dāng)前事件。 -
boolean onInterceptTouchEvent (MotionEvent event)
在上述方法內(nèi)部調(diào)用,用來判斷是否攔截某個事件。如果當(dāng)前View攔截了某個事件,那么在同一個事件序列過程中,該方法不會被再次被調(diào)用。返回結(jié)果表示是否攔截當(dāng)前事件。 -
boolean onTouchEvent (MotionEvent event)
在dispatchTouchEvent方法中調(diào)用,用來處理當(dāng)前事件。其中,MotionEvent.ACTION_UP時會調(diào)用performClick方法,即mOnClickListener.onClick(this)方法。返回結(jié)果表示是否消耗當(dāng)前事件,如果不消耗,則在同一個事件序列過程中,當(dāng)前View無法再次接收到事件。
偽代碼表示事件傳遞規(guī)則:
ViewGroup的分發(fā)過程
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的分發(fā)過程
public boolean dispatchTouchEvent(MotionEvent event) {
boolean consume = false;
if (mOnTouchListener.onTouch(this, event)) {
consume = true;
} else {
consume = onTouchEvent(event);
}
return consume;
}
demo效果

默認(rèn)情況.png

父容器攔截事件.png

子View onTouchEvent返回false.png
結(jié)論:
- 事件處理優(yōu)先級比較:OnTouchListener.onTouch > onTouchEvent > OnClickListener.onClick。
- 同一個事件序列是指從手指觸摸屏幕的那一刻起,到手指離開屏幕的那一刻結(jié)束,在這個過程中所產(chǎn)生的一系列事件。這個事件序列:down -- 若干個move -- up。
- 事件傳遞流程:Activity -> Window -> View。
- 當(dāng)一個View攔截一個事件后,那么這個事件序列都只能由它來處理,并且它的onInterceptTouchEvent方法不會再被調(diào)用。
- 正常情況下,一個事件序列只能被一個View攔截且消耗(特殊情況:View通過onTouchEvent將需要自己處理的事件強行傳遞給其他View)。
- 某個View一旦開始處理事件,如果它不消耗ACTION_DOWN事件(onTouchEvent返回false),那么同一事件序列中的其他事件都不會交給它來處理,而是重新交給它的父元素處理。
- 如果View不消耗ACTION_DOWN以外的其他事件,那么點擊事件會消失,此時父元素的onTouchEvent并不會被調(diào)用,并且當(dāng)前View可以持續(xù)收到后續(xù)事件,最終這些點擊事件交給Activity處理。
- ViewGroup默認(rèn)不攔截任何事件。
- View沒有onInterceptTouchEvent方法。
- View的onTouchEvent默認(rèn)都會消耗事件(返回true),除非它是不可點擊的。
應(yīng)用:滑動沖突的解決
- 外部攔截法:重寫父容器的onInterceptTouchEvent方法,判斷是否需要攔截
- 內(nèi)部攔截法:重寫子View的dispatchTouchEvent方法,判斷是否消耗掉事件