看Android項(xiàng)目源碼了解到的不足——事件分發(fā)機(jī)制

其實(shí)一直對(duì)Android的分發(fā)機(jī)制沒有深入的了解,就只是會(huì)用onClientListener()方法。不知道為什么這么用,所以今天就深入了解一下,Android的事件分發(fā)機(jī)制。

什么是Android的事件分發(fā)?

android事件分發(fā)機(jī)制 就是一個(gè)觸摸事件發(fā)生了,從一個(gè)窗口傳遞到一個(gè)視圖,再傳遞到另外一個(gè)視圖,最后被消費(fèi)的過程。

事件分發(fā)的主角是?

Touch事件分發(fā)中只有兩個(gè)主角:ViewGroup和View。Activity的Touch事件事實(shí)上是調(diào)用它內(nèi)部的ViewGroup的Touch事件,可以直接當(dāng)成ViewGroup處理。

View在ViewGroup內(nèi),ViewGroup也可以在其他ViewGroup內(nèi),這時(shí)候把內(nèi)部的ViewGroup當(dāng)成View來分析。

ViewGroup的相關(guān)事件有三個(gè):onInterceptTouchEvent、dispatchTouchEvent、onTouchEvent。View的相關(guān)事件只有兩個(gè):dispatchTouchEvent、onTouchEvent。

事件分發(fā)的過程

(1) 事件從Activity.dispatchTouchEvent()開始傳遞,只要沒有被停止或攔截,從最上層的View(ViewGroup)開始一直往下(子View)傳遞。子View可以通過onTouchEvent()對(duì)事件進(jìn)行處理。

(2) 事件由父View(ViewGroup)傳遞給子View,ViewGroup可以通過onInterceptTouchEvent()對(duì)事件做攔截,停止其往下傳遞。

(3) 如果事件從上往下傳遞過程中一直沒有被停止,且最底層子View沒有消費(fèi)事件,事件會(huì)反向往上傳遞,這時(shí)父View(ViewGroup)可以進(jìn)行消費(fèi),如果還是沒有被消費(fèi)的話,最后會(huì)到Activity的onTouchEvent()函數(shù)。

(4) 如果View沒有對(duì)ACTION_DOWN進(jìn)行消費(fèi),之后的其他事件不會(huì)傳遞過來。

(5) OnTouchListener優(yōu)先于onTouchEvent()對(duì)事件進(jìn)行消費(fèi)。

事件分發(fā)注意事項(xiàng)

事件分發(fā)在View中的處理:

1.onTouch是優(yōu)先于onClick執(zhí)行的,并且onTouch執(zhí)行了兩次,一次是ACTION_DOWN,一次是ACTION_UP(你還可能會(huì)有多次ACTION_MOVE的執(zhí)行,如果你手抖了一下)。因此事件傳遞的順序是先經(jīng)過onTouch,再傳遞到onClick。

2.只要你觸摸到了任何一個(gè)控件,就一定會(huì)調(diào)用該控件的dispatchTouchEvent方法。那當(dāng)我們?nèi)c(diǎn)擊按鈕的時(shí)候,就會(huì)去調(diào)用Button類里的dispatchTouchEvent方法,可是你會(huì)發(fā)現(xiàn)Button類里并沒有這個(gè)方法,那么就到它的父類TextView里去找一找,你會(huì)發(fā)現(xiàn)TextView里也沒有這個(gè)方法,那沒辦法了,只好繼續(xù)在TextView的父類View里找一找,這個(gè)時(shí)候你終于在View里找到了這個(gè)方法,示意圖如下:

3.首先在dispatchTouchEvent中最先執(zhí)行的就是onTouch方法,因此onTouch肯定是要優(yōu)先于onClick執(zhí)行的。而如果在onTouch方法里返回了true,就會(huì)讓dispatchTouchEvent方法直接返回true,不會(huì)再繼續(xù)往下執(zhí)行,onClick就不會(huì)再執(zhí)行了。

參考:http://blog.csdn.net/guolin_blog/article/details/9097463

事件分發(fā)在ViewGroup中的處理:

1.只要你觸摸了任何控件,就一定會(huì)調(diào)用該控件的dispatchTouchEvent方法。這個(gè)說法沒錯(cuò),只不過還不完整而已。實(shí)際情況是,當(dāng)你點(diǎn)擊了某個(gè)控件,首先會(huì)去調(diào)用該控件所在布局的dispatchTouchEvent方法,然后在布局的dispatchTouchEvent方法中找到被點(diǎn)擊的相應(yīng)控件,再去調(diào)用該控件的dispatchTouchEvent方法。如果我們點(diǎn)擊了MyLayout中的按鈕,會(huì)先去調(diào)用MyLayout的dispatchTouchEvent方法,可是你會(huì)發(fā)現(xiàn)MyLayout中并沒有這個(gè)方法。那就再到它的父類LinearLayout中找一找,發(fā)現(xiàn)也沒有這個(gè)方法。那只好繼續(xù)再找LinearLayout的父類ViewGroup,你終于在ViewGroup中看到了這個(gè)方法,按鈕的dispatchTouchEvent方法就是在這里調(diào)用的。修改后的示意圖如下所示:


參考:http://blog.csdn.net/guolin_blog/article/details/9153761

總結(jié):

1. Android事件分發(fā)是先傳遞到ViewGroup,再由ViewGroup傳遞到View的。

2. 在ViewGroup中可以通過onInterceptTouchEvent方法對(duì)事件傳遞進(jìn)行攔截,onInterceptTouchEvent方法返回true代表不允許事件繼續(xù)向子View傳遞,返回false代表不對(duì)事件進(jìn)行攔截,默認(rèn)返回false。

3. 子View中如果將傳遞的事件消費(fèi)掉,ViewGroup中將無法接收到任何事件

什么是滑動(dòng)沖突?

滑動(dòng)沖突,就其本質(zhì)來說,兩個(gè)不同方向(或者是同方向)的View,其中有一個(gè)是占主導(dǎo)地位的,每次總是搶著去處理外界的滑動(dòng)行為,這樣就導(dǎo)致一種很別扭的用戶體驗(yàn),明明只是橫向的滑動(dòng)了一下,縱向的列表卻在垂直方向發(fā)生了動(dòng)作。就是說,這個(gè)占主導(dǎo)地位的View,每一次都身不由己的攔截了這個(gè)滑動(dòng)的動(dòng)作,因此,要解決滑動(dòng)沖突,就是得明確告訴這個(gè)占主導(dǎo)地位的View,什么時(shí)候你該攔截,什么時(shí)候你不應(yīng)該攔截,應(yīng)該由下一層的View去處理這個(gè)滑動(dòng)動(dòng)作。

滑動(dòng)沖突的解決方案:解決滑動(dòng)沖突的關(guān)鍵,就是明確告知接收到Touch的View,是否需要攔截此次事件。

外部攔截法和內(nèi)部攔截法

1.外部,故名思議是在父View的onInterceptTouchEvent處理.它針對(duì)3種不同的事件做處理,對(duì)于down, 返回false,除非你希望那個(gè)讓父View完全處理這3個(gè)事件。由于這里是false.所以同一事件序列的其他2個(gè)事件父view肯定能執(zhí)行到(除非設(shè)置一個(gè)tag),對(duì)于move,看業(yè)務(wù)情況,返回true代表父類來消耗,false則表示子類,對(duì)于up,返回false.除非你想讓子View的click這種都無法用。

2.內(nèi)部,重寫子元素的dispatchTouchEvent方法。默認(rèn)情況下父View可以寫成除了down,其他都攔截,然后在子View里用parent,requestDisallowInterceptTouchEvent()

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容