1、View的事件分發(fā)涉及到3個核心的方法:
1.1、第一個是dispatchtouchevent,這個方法是用來進行事件分發(fā),如果事件能夠傳遞給當前 view, 那么此方法一定被調(diào)用,它的返回值受當前view的ontouchevent和下級view的 dispatchevent的影響,表示是否消耗當前事件;
1.2、第二個方法是oninterceptouchevent,這個方法在 dispatchtouchevent方法內(nèi)部調(diào)用,用來判斷是否攔截某個事件,如果當前view攔截了某個事件 ,那么在同一事件序列中 ,此方法不會 再次被調(diào)用,返回的結(jié)果表示是否攔截當前事件;
1.3、第三個方法是ontouchevent,這個方法也是在dispatchtouchevent方法中調(diào)用,用來處理事件,返回的結(jié)果表示是否消耗當前事件,如果不消耗,同一 事件序列中 ,當前view無法再次接 受到此事件序列中的后續(xù)事件。
三個方法的聯(lián)系可用一下偽代碼生動描述
public boolean dispatchTouchEvent(MotionEvent event) {
boolean consume = false;
if(onInterceptTouchEvent(ev)) {
consume = onTouchEvent(ev);
} else {
consume = child.dispatchTouchEvent(ev);
}
return consume;
}
2、具體過程
具體來說,對于一個根 viewgroup來說,事件產(chǎn)生后,首先會傳遞給它,這時它的dispatchtouchevent方法會被調(diào)用,如果這個viewgroup的interceptedtouchevent方法返回為 true,就表示它要攔截當前事件,那么它的 ontouchevent方法會被調(diào)用,去處理這個事件 ;
如果這個viewgroup的oninterceptouchevent方法返回為 false,就表示它不攔截當前事件, 這時當前事件就會繼續(xù)傳遞給它的子view,接著子view的dispatchtouchevent方法就會被調(diào)用,如此往下,直至事件最終被處理。
當一個 view開始處理某個事件,如果它設(shè)置了ontouchlistener, 那么它的ontouch方法會被調(diào)用,如果ontouch返回為true,那么view的ontouchevent方法不會被調(diào)用,如果返回為false,當前view的ontouchevent方法會被調(diào)用,如果當前view還設(shè)置了onclicklistener,那么onclick也會被隨后調(diào)用。
3、事件分發(fā)的一些結(jié)論
(1)、若 ViewGroup 攔截了一個半路的事件(如MOVE),該事件將會被系統(tǒng)變成一個CANCEL事件 & 傳遞給之前處理該事件的子View;
該事件不會再傳遞給ViewGroup的onTouchEvent();
只有再到來的事件才會傳遞到ViewGroup的onTouchEvent()。
因為一旦當前事件變成取消事件,mFirstTouchTarget就會被置為null。
(2)、ViewGroup的onInterceptTouchEvent()對事件返回了false,但后續(xù)的事件(MOVE、UP)依然會傳遞給它的onInterceptTouchEvent() ;只有在onInterceptTouchEvent方法返回為true,此方法將不會被調(diào)用。
(3)、某個View一旦開始處理事件,如果它不消耗ACTION_DOWN事件(onTouchEvent()返回了false),那么同一事件序列中的其他事件都不會再交給它處理,并且事件將重新交由它的父元素去處理,即父元素的onTouchEvent()會被調(diào)用。
(4)、ViewGroup默認不攔截事件。onInterceptTouchEvent()方法默認返回false。
(5)、View沒有onInterceptTouchEvent()方法,一旦有事件傳遞給它,那么它的onTouchEvent()方法就會被調(diào)用。
(6)、View的onTouchevent()默認都會消耗事件(返回true),除非它是不可點擊的(clickable和longClickable同時為false)。
(7)、onClick會發(fā)生的前提是當前View是可點擊的,并且它收到了down和up的事件。
(8)、當面對ACTION_DOWN事件時,ViewGroup總是會調(diào)用自己的onInterceptTouchEvent()方法來詢問自己是否要攔截該事件。
以下是安卓藝術(shù)探索沒說清楚的:
(9)、正常情況下,一個事件序列只能被一個View攔截并消耗。(異常情況對應(yīng)第1條)
(10)某個View一旦決定攔截,那么這一個事件序列都只能由它來處理(如果事件序列能夠傳遞給它的話,異常情況對應(yīng)第一條),并且它的onInterceptTouchEvent()不會再被調(diào)用。
(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方法才會去調(diào)用,(ViewGroup在兩種情況下會去判斷是否攔截事件(ACTION_DOWN || mFirstTouchTarget != null)),反過來說就是: 當當前事件是ACTION_MOVE和ACTION_UP事件時,如果子View沒有處理down事件(mFirstTouchTarget==null),(第一種情況:是父view的onInterceptTouchEvent返回true,處理了事件;第一種情況:是子view沒有消耗down事件。不管是哪種情況)
ViewGroup的onInterceptTouchEvent不會再被調(diào)用,后續(xù)事件都會默認交給父view處理;子view也無法接收到后續(xù)事件。
1,view滑動總結(jié):http://www.itdecent.cn/p/61ad263c4a0e
2,view滑動沖突
2,Android事件分發(fā)機制 詳解攻略,您值得擁有:https://blog.csdn.net/carson_ho/article/details/54136311
3,事件分發(fā)機制全解析https://juejin.cn/post/6844903849723953166