android的事件分發(fā)機(jī)制其實(shí)跟實(shí)現(xiàn)中的工作流程很相似,比如有一個(gè)問(wèn)題下來(lái),最先知道這個(gè)問(wèn)題的是最高層的領(lǐng)導(dǎo),領(lǐng)導(dǎo)如果不攔截這個(gè)問(wèn)題就會(huì)把問(wèn)題向下級(jí)分發(fā),直到有人把問(wèn)題處理掉,或者到最后一級(jí)都不處理,那么問(wèn)題就會(huì)交給領(lǐng)導(dǎo)處理,領(lǐng)導(dǎo)還處理不了,就會(huì)交給領(lǐng)導(dǎo)的領(lǐng)導(dǎo)。。。其實(shí)就是一個(gè)V字形。
廢話不多說(shuō)了,進(jìn)入正題。。。
1、點(diǎn)擊事件的傳遞規(guī)則
其實(shí)就是MotionEvent的傳遞過(guò)程,這里由三個(gè)方法來(lái)共同完成:dispatchTouchEvent和onInterceptTouchEvent、onTouchEvent。
<b> public boolean dispatchTouchEvent(MotionEvent event) </b>
這個(gè)方法用來(lái)進(jìn)行事件的分發(fā),如果事件能傳遞到view,那么view的這個(gè)方法一定會(huì)被調(diào)用,它的返回結(jié)果受到當(dāng)前view的onTouchEvent和下級(jí)的dispatchTouchEvent方法的影響,表示是否消耗掉當(dāng)前事件。
<b> public boolean onInterceptTouchEvent(MotionEvent event) </b>
在上述方法中被調(diào)用,用來(lái)判斷是否攔截當(dāng)前事件,如果當(dāng)前view攔截個(gè)某個(gè)事件,那么同一個(gè)事件序列中,此方法不會(huì)被再次調(diào)用,返回結(jié)果表示是否攔截當(dāng)前事件。
<b> public boolean onTouchEvent(MotionEvent event) </b>
在dispatchTouchEvent方法中被調(diào)用,用來(lái)處理點(diǎn)擊事件,返回結(jié)果表示是否消耗當(dāng)前事件,如果不消耗,那么同一個(gè)事件序列中,當(dāng)前view無(wú)法再接收到事件。
上述三個(gè)方法的關(guān)系用如下偽代碼表示:
public boolean dispatchTouchEvent(MotionEvent event) {
boolean consume = false;
if(onIntercepTouchEvent(event)){
consume = onTouchEvent(event);
} else {
consume = child.dispatchTouchEvent(event);
}
return consume;
}
通過(guò)上面的偽代碼,可以大致了解點(diǎn)擊事件的傳遞規(guī)則:對(duì)于一個(gè)根viewgroup來(lái)說(shuō),點(diǎn)擊事件產(chǎn)生后,首先會(huì)傳遞給它,這時(shí)它的dispatchTouchEvent方法會(huì)被調(diào)用,如果這個(gè)viewgroup的onIntercepTouchEvent方法返回true的話,這個(gè)事件就會(huì)由它的onTouchEvent方法消耗掉,否則它會(huì)將點(diǎn)擊事件傳遞下去。
當(dāng)一個(gè)view需要處理事件時(shí),如果它設(shè)置了OnTouchListener,那么OnTouchListener中的onTouch方法會(huì)被調(diào)用。這時(shí)事件如何處理還要看onTouch的返回值,如果返回false,則當(dāng)前view的onTouchEvent方法會(huì)被調(diào)用,如果返回true,那么onTouchEvent方法將不會(huì)被調(diào)用??梢钥闯?,平時(shí)我們常用的OnClickListener,其優(yōu)先級(jí)最低,即處于事件傳遞的尾端。
當(dāng)一個(gè)點(diǎn)擊事件產(chǎn)生后,它的傳遞過(guò)程遵循如下順序:Activity ->Window ->View,即事件總是先傳遞給Activity,Activity再傳遞給window,最后Window再傳遞給頂級(jí)View,頂級(jí)View接收事件后,就會(huì)按照事件分發(fā)機(jī)制去分發(fā)事件??紤]一種情況,如果一個(gè)View的onTouchEvent返回false,那么它的父容器的onTouchEvent將會(huì)被調(diào)用,依此類推,如果所有的元素都不處理這個(gè)事件,那么這個(gè)事件將會(huì)最終傳遞給Activity處理,即Activity的onTouchEvent方法會(huì)調(diào)用。這個(gè)過(guò)程其實(shí)也很好理解,我們可以換一種思路,假如點(diǎn)擊事件是一個(gè)難題,這個(gè)難題最終被上級(jí)領(lǐng)導(dǎo)分給一個(gè)程序員去處理(這是事件分發(fā)過(guò)程),結(jié)果這個(gè)程序員搞不定(onTouchEvent返回了false),現(xiàn)在該怎么辦呢?難題必須要解決,那只能交給水平更高的上級(jí)解決(上級(jí)的onTouchEvent被調(diào)用),如果上級(jí)再搞不定,那只能交給上級(jí)的上級(jí)去解決,就這樣將難題一層層地向上拋,這是公司內(nèi)部一種很常見(jiàn)的處理問(wèn)題的過(guò)程。從這個(gè)角度來(lái)看,View的事件傳遞過(guò)程還是很貼近現(xiàn)實(shí)的,畢竟程序員也生活在現(xiàn)實(shí)中。
關(guān)于事件傳遞的機(jī)制,這里給出一些結(jié)論,根據(jù)這些結(jié)論可以更好地理解整個(gè)傳遞機(jī)制,如下所示。
(1)同一個(gè)事件序列是指從手指接觸屏幕的那一該走,到手指離開屏幕的那一該結(jié)束,在這個(gè)過(guò)程中所產(chǎn)生的一系列事件,這個(gè)事件序列以down事件開始,中間含有數(shù)量不定的move事件,最終以u(píng)p事件結(jié)束。
(2)正常情況下,一個(gè)事件序列只能被一個(gè)View攔截且消耗。這一條的原因可以參考(3),因?yàn)橐坏┮粋€(gè)元素?cái)r截了此事件,那么同一個(gè)事件序列內(nèi)的所有事件都會(huì)直接交給它處理,因此同一個(gè)事件序列中的事件不能分別由兩個(gè)View處理,但是通過(guò)特殊手段可以做到,比如一個(gè)View將本該自己處理的事件通過(guò)onTouchEvent強(qiáng)行傳遞給其他View處理。
(3)某個(gè)View一旦決定攔截,那么這一個(gè)事件序列都只能由它來(lái)處理(如果事件序列能夠傳遞給它的話),并且它的onInterceptTouchEvent不會(huì)再被調(diào)用。這條也很好理解,就是說(shuō)當(dāng)一個(gè)View決定攔截一個(gè)事件后,那么系統(tǒng)會(huì)把同一個(gè)事件序列內(nèi)的其他方法都直接交給它來(lái)處理,因此就不用再調(diào)用這個(gè)View的onInterceptTouchEvent去詢問(wèn)它是否要攔截了。
(4)某個(gè)View一旦開始處理事件,如果它不消耗ACTION_DOWN事件(onTouchEvent返回了false),那么同一個(gè)序列中的其他事件都不會(huì)再交給它來(lái)處理,并且事件將重新交由它的父元素去處理,并且事件將重新交由它的父元素處理,即父元素的onTouchEvent會(huì)被調(diào)用。也就是事件一旦交給一個(gè)View處理,那么它就必須消耗掉,否則同一事件序列中剩下的事件就不再交給它來(lái)處理了,這就好比上級(jí)交給程序員一件事,如果這件事沒(méi)有處理好,短期內(nèi)上級(jí)就不敢再把事件交給這個(gè)程序員做了,二者是類似的道理。
(5)如果View不消耗除ACTION_DOWN以外的其他事件,那么這個(gè)點(diǎn)擊事件會(huì)消失,此時(shí)父元素的onTouchEvent并不會(huì)被調(diào)用,并且當(dāng)前View可以持續(xù)收到后續(xù)的事件,最終這些消失的點(diǎn)擊事件會(huì)傳遞給Activity處理。
(6)ViewGroup默認(rèn)不攔截任何事件。Android源碼中ViewGroup的onInterceptTouchEvent方法默認(rèn)返回false。
(7)View沒(méi)有onIntercepTouchEvent方法,一旦有點(diǎn)擊事件傳遞給它,那么它的onTouchEvent方法默認(rèn)返回false。
(8)View的onTouchEvent默認(rèn)都會(huì)消耗事件(返回true),除非它是不可點(diǎn)擊的(clickable和longClickable同時(shí)為false)。View的longClickable屬性默認(rèn)都為false,clickable屬性要分情況,比如Button的clickable屬性默認(rèn)為true,而TextView的clickable屬性默認(rèn)為false。
(9)View的enable屬性不影響onTouchEvent的默認(rèn)返回值。哪怕一個(gè)View是disable狀態(tài)的,只要它的clickable或者longClickable有一個(gè)為true,那么它的onTouchEvent就返回true。
(10)onClick會(huì)發(fā)生的前提是當(dāng)前View是可點(diǎn)擊的,并且它收到了down和up的事件。
(11)事件傳遞過(guò)程是由外向內(nèi)的,即事件總先傳遞給父元素,然后再由父元素分發(fā)給子View,通過(guò)requestDisallowInterceptTouchEvent方法可以在子元素中干預(yù)父元素的事件分發(fā)過(guò)程,但是ACTION_DOWN事件除外。