整個(gè)事件分發(fā)的流程圖如下擒抛,現(xiàn)在我們根據(jù)代碼去一步步畫(huà)出整個(gè)事件分發(fā)的流程圖推汽。
事件方法 | dispatchTouchEvent | onInterceptTouchEvent | onTouchEvent |
---|---|---|---|
ViewGroup | 有 | 有 | 無(wú)(繼承View ,在view中可以找到) |
View | 有 | 無(wú) | 有 |
下面代碼是抽出來(lái)的整個(gè)事件分發(fā)的核心偽代碼:
// Activity中該方法的核心部分偽代碼
1 public boolean dispatchTouchEvent(MotionEvent ev) {
2 if (child.dispatchTouchEvent(ev)) {
//如果子View消費(fèi)了該事件,則返回TRUE歧沪,讓調(diào)用者知道該事件已被消費(fèi)
3 return true;
4 } else {
//如果子View沒(méi)有消費(fèi)該事件歹撒,則調(diào)用自身的onTouchEvent嘗試處理。
5 return onTouchEvent(ev);
6 }
7}
// ViewGroup_1 中該方法的核心部分偽代碼
8 public boolean dispatchTouchEvent(MotionEvent ev) {
9 if (!onInterceptTouchEvent(ev)) {
//不攔截诊胞,則傳給子View進(jìn)行分發(fā)處理
10 return child.dispatchTouchEvent(ev);
11 } else {
//攔截事件暖夭,交由自身對(duì)象的onTouchEvent方法處理
12 return onTouchEvent(ev);
13 }
14}
// ViewGroup_2中該方法的核心部分偽代碼
15 public boolean dispatchTouchEvent(MotionEvent ev) {
16 if (!onInterceptTouchEvent(ev)) {
//不攔截,則傳給子View進(jìn)行分發(fā)處理
17 return child.dispatchTouchEvent(ev);
18 } else {
//攔截事件撵孤,交由自身對(duì)象的onTouchEvent方法處理
19 return onTouchEvent(ev);
20 }
21 }
// View中該方法的核心部分偽代碼
22 public boolean dispatchTouchEvent(MotionEvent ev) {
23 //如果該對(duì)象的監(jiān)聽(tīng)成員變量不為空鳞尔,則會(huì)調(diào)用其onTouch方法,
24 if (mOnTouchListener != null && mOnTouchListener.onTouch(this, event)) {
25 return true; //若onTouch方法返回TRUE早直,則表示消費(fèi)了該事件寥假,則dispachtouTouchEvent返回TRUE,讓其調(diào)用者知道該事件已被消費(fèi)霞扬。
26 }
27 return onTouchEvent(ev); //若監(jiān)聽(tīng)成員為空或onTouch沒(méi)有消費(fèi)該事件糕韧,則調(diào)用對(duì)象自身的onTouchEvent方法處理。
28 }
我們就對(duì)照流程圖和核心偽代碼來(lái)分析整個(gè)流程喻圃。然后自己可以一步步畫(huà)出流程圖
都知道當(dāng)我們按下觸摸到屏幕 事件是從驅(qū)動(dòng)的 dev/input/event[x] 開(kāi)始的萤彩。
事件一直傳遞到Activity的dispatchTouchEvent中。好了斧拍,這塊不是我們今天的重點(diǎn)雀扶。
先來(lái)看Activity中dispatchTouchEvent 事件到達(dá)這里后 如果2返回true 就表示消費(fèi)事件, flase 那么就走它自己的 onTouchEvent肆汹。
代碼就像這樣愚墓。
1.從Activity->ViewGroup
// Activity中該方法的核心部分偽代碼
1 public boolean dispatchTouchEvent(MotionEvent ev) {
2 if (child.dispatchTouchEvent(ev)) {
3 return true; //如果子View消費(fèi)了該事件,則返回TRUE,讓調(diào)用者知道該事件已被消費(fèi)
4 } else {
5 return onTouchEvent(ev); //如果子View沒(méi)有消費(fèi)該事件昂勉,則調(diào)用自身的onTouchEvent嘗試處理浪册。
6 }
7 }
// 重寫ViewGroup_1 中的該方法
8 public boolean dispatchTouchEvent(MotionEvent ev) {
9 //return flase;
9 //return true;
10 }
對(duì)應(yīng)的流程圖如下
2.從ViewGroup->View
//繼續(xù)看 ViewGroup_1 中該方法的核心部分偽代碼
8 public boolean dispatchTouchEvent(MotionEvent ev) {
9 if (!onInterceptTouchEvent(ev)) { //取反
10 return child.dispatchTouchEvent(ev); //不攔截,則傳給子View進(jìn)行分發(fā)處理
11 } else {
12 return onTouchEvent(ev); //攔截事件岗照,交由自身對(duì)象的onTouchEvent方法處理()
13 }
14}
如果onInterceptTouchEvent(ev) 返回true 那么就會(huì)走自己(12)的繼承類view的onTouchEvent(ev)村象,表示ViewGroup_1要 攔截這個(gè)事件。
如果onInterceptTouchEvent(ev) 返回false 那么就會(huì)走child.dispatchTouchEvent(ev) 攒至,ViewGroup_1 不攔截這個(gè)事件,繼續(xù)往下傳遞厚者。默認(rèn)false。
對(duì)應(yīng)的流程圖如下:
3.從View到onTouchEvent
然后到了調(diào)用到了child.dispatchTouchEvent(ev),Child = View
22 public boolean dispatchTouchEvent(MotionEvent ev) {
23 //如果該對(duì)象的監(jiān)聽(tīng)成員變量不為空迫吐,則會(huì)調(diào)用其onTouch方法库菲,
24 if (mOnTouchListener != null && mOnTouchListener.onTouch(this, event)) {
//若onTouch方法返回TRUE,則表示消費(fèi)了該事件渠抹,則dispachtouTouchEvent返回TRUE蝙昙,讓其調(diào)用者知道該事件已被消費(fèi)闪萄。
25 return true;
26 }
//若監(jiān)聽(tīng)成員為空或onTouch沒(méi)有消費(fèi)該事件,則調(diào)用對(duì)象自身的onTouchEvent方法處理奇颠。
27 return onTouchEvent(ev);
28 }
對(duì)應(yīng)的完整的流程圖如下败去。
其中如果我們不做任何干預(yù)的話流程會(huì)是:1->2->3->4->5->6->7。
流程圖畫(huà)完了烈拒,可以大腦里面再整理一下圆裕。整個(gè)流程還是很簡(jiǎn)單的 采用遞歸的思想去處理事件分發(fā)的過(guò)程。
----------------事件的調(diào)用順序--------------------
父ViewGroup dispatchTocuhEvent
父ViewGroup onInterceptionTouchEvent
子ViewGroup dispatchTocuhEvent
子ViewGroup onInterceptionTouchEvent
子 onTouchEvent
父 onTouchEvent
結(jié)論:
事件沖突的規(guī)律解決:
1.(外部攔截 “推薦的做法”)在父ViewGroup 做攔截操作:那么父ViewGroup需要重寫onInterceptionTouchEvent 做邏輯判斷攔截就 在down 返回true 荆几,并且在onTouchEvent中一定要返回true消費(fèi)此次事件吓妆,不然會(huì)收不到后續(xù)事件。
2.(內(nèi)部攔截)在子ViewGroup做攔截操作:那么子ViewGroup可以重寫onDispatchTocuhEvent 做邏輯判斷吨铸。攔截的話 就 需要調(diào)用在dispatchTocuhEvent中down中調(diào)用requestDisllowInterceptionTouchEvent(true) 才能攔截下次move之后的事件行拢,告訴父ViewGroup的onInterceptionTouchEvent 你不要攔截 那么就可以走我的onDispatchTocuhEvent 事件。
requestDisllowInterceptionTouchEvent() 是無(wú)法攔截 父ViewGroup的down事件的诞吱,因?yàn)樵贒own事件的時(shí)候ViewGroup會(huì)清除掉所有View事件的標(biāo)記舟奠。只能攔截 父ViewGroup Down之后的事件
dispatchTocuhEvent 部分代碼,對(duì)以上結(jié)論說(shuō)明:
if (actionMasked == MotionEvent.ACTION_DOWN) {
//down清除標(biāo)記位,然后分發(fā)事件
cancelAndClearTouchTargets(ev);
resetTouchState();
}
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
//FLAG_DISALLOW_INTERCEPT 房维, disallowIntercept = true
if (!disallowIntercept) {
//如果父ViewGroup 重寫onInterceptTouchEvent 默認(rèn)返回true沼瘫,那么子VIew還想收到事件那么就不讓你進(jìn)這里來(lái)
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action);
} else {//走這里那么子View又可以愉快的相應(yīng)down之后的事件拉
intercepted = false;
}
} else {
intercepted = true;
}
if (!canceled && !intercepted) {
//處理事件分發(fā)
}