Android事件傳遞機(jī)制
關(guān)于Android中的事件傳遞肚吏,在Android系統(tǒng)源代碼層級(jí)的實(shí)現(xiàn)上非常的復(fù)雜,而對于應(yīng)用程序的開發(fā)而言槽华,我們倒不必要深究太多的細(xì)節(jié)忙灼,我們只需要掌握事件傳遞機(jī)制所帶來的一些結(jié)論即可。這篇文章通過三種事件模型來聊聊關(guān)于事件傳遞機(jī)制的一些知識(shí)點(diǎn)家夺。閱讀本文需要大家靜下心來耐心閱讀脱柱,必定會(huì)有所收獲。
在正式開始之前拉馋,大伙可以先記著幾個(gè)結(jié)論榨为,這樣可以便于大家理解。
結(jié)論1:事件的一定是先到達(dá)父控件上
結(jié)論2:事件簡單來說可以分為三種:Down事件煌茴、Move事件随闺、Up事件,結(jié)合結(jié)論1可以得到蔓腐,Down事件最先到達(dá)父控件上矩乐,Move事件也是最先到達(dá)父控件上,Up事件也是最先到達(dá)父控件上的回论。
結(jié)論3:父控件和父類不是一回事绰精,這兩個(gè)概念初學(xué)者很容易混淆
**事件模型1:父控件 ——> 子控件
關(guān)于這種事件模型主要涉及到3個(gè)概念:
事件的分發(fā)
事件的攔截
事件的響應(yīng)
我們先把基礎(chǔ)知識(shí)給回顧一下撒璧,事件分發(fā)是dispatchTouchEvent,事件攔截是onInterceptToucllhEvent笨使,事件的響應(yīng)是onTouchEvent卿樱,對于ViewGroup類型的控件來說,它擁有這三種方法硫椰,而對于單個(gè)View控件來說的話繁调,它只有dispatchTouchEvent和onTouchEvent,因?yàn)閂iew不能包含其他的View靶草,所以不需要判斷是否要攔截事件蹄胰。
一個(gè)事件如果到達(dá)了某一個(gè)View或者ViewGroup,那么一定會(huì)最先調(diào)用到這個(gè)控件的dispatchTouchEvent.
dispatchTouchEvent這個(gè)方法就是含義就是把一個(gè)事件給分發(fā)下去奕翔,那么它具體分發(fā)的邏輯是怎樣的呢裕寨?
圖1
A、 它首先會(huì)先調(diào)用自身的onInterceptTouchEvent方法派继,調(diào)用此方法的目的是為了先讓自己這個(gè)控件判斷下是否需要把此事件攔截下來宾袜,如果攔截下來,那么就代表自己這個(gè)控件需要來處理這個(gè)事件驾窟,所以此時(shí)會(huì)調(diào)用自身了onTouchEvent來對這個(gè)事件進(jìn)行響應(yīng)庆猫。
圖2
B、 如果不攔截下來绅络,那么才會(huì)有后續(xù)的事件向下傳遞的流程月培。將這個(gè)事件傳遞給子控件。現(xiàn)在子控件接收到了這個(gè)事件恩急,按照剛剛的說法杉畜,一個(gè)事件到達(dá)了一個(gè)View或者ViewGroup,就會(huì)最先調(diào)用這個(gè)控件的dispatchTouchEvent衷恭,所以此時(shí)此叠,事件到達(dá)了子控件的dispatchTouchEvent方法,如果這個(gè)控件仍然是一個(gè)ViewGroup的類型匾荆,那么事件繼續(xù)分發(fā)的邏輯依然遵循A流程的邏輯荤西。
圖3
C瘪弓、 如果這個(gè)子控件只是一個(gè)View夜郁,而不是ViewGroup叉橱,那么此時(shí)事件分發(fā)的邏輯略有不同驶乾。由于View是沒有onInterceptTouchEvent的方法栈妆,所以當(dāng)一個(gè)事件到達(dá)這個(gè)View的dispatchTouchEvent的時(shí)候愕提,這時(shí)dispatchTouchEvent就調(diào)用不到onInterceptTouchEvent了锐墙,它會(huì)直接調(diào)用onTouchEvent的方法析校,直接讓這個(gè)View來響應(yīng)此事件构罗。
圖4
通過ABC三個(gè)流程铜涉,我們就能夠非常清楚的清楚事件傳遞的邏輯。父控件傳遞給子控件遂唧,當(dāng)沒有控件攔截事件的話芙代,將會(huì)一層層的向下傳遞,直到最后一個(gè)子控件盖彭。
事件傳遞的流程說完之后纹烹,我們就來說一說事件響應(yīng)的方向。我們知道onTouchEvent也是有返回值的召边,要么是true铺呵,要么是false,不同的返回值會(huì)有不同的表現(xiàn)隧熙。
圖5
如上圖所示片挂,如果ViewGroupB攔截了事件,那么此時(shí)事件就會(huì)由ViewGroupB來響應(yīng)贞盯,調(diào)用ViewGroupB中的onTouchEvent音念,此時(shí)ViewGroupB中的onTouchEvent的返回值有兩種可能,一種是true邻悬,一種是false症昏,如果返回true,則代表父丰,ViewGroupB消費(fèi)了此事件肝谭,事件此時(shí)終止。如果返回的值是false蛾扇,那么此時(shí)這個(gè)事件會(huì)回傳給父控件攘烛,調(diào)用到父控件的onTouchEvent方法,由父控件來進(jìn)行響應(yīng)镀首,那父控件的onTouchEvent也是同樣的邏輯坟漱。要么消費(fèi)事件,要么回傳給父控件的父控件更哄。
所以此時(shí)芋齿,就可以得出我們通常所說的兩個(gè)方向:
1、 事件傳遞的方向:父控件****à****子控件
2成翩、 事件響應(yīng)的方向****:子控件****à****父控件
更加清晰的圖:
圖6
當(dāng)然僅僅是這個(gè)結(jié)論是無法滿足我們實(shí)際開發(fā)的需要觅捆,我們需要更細(xì)致的分析。這里有一個(gè)細(xì)節(jié)上的問題需要注意麻敌,就是事件分為Down事件栅炒、Move事件、Up事件,任何一種事件都遵循事件傳遞和響應(yīng)的邏輯原則赢赊,很多同學(xué)常常會(huì)認(rèn)為 Down-Move-Up 這連在一起才是一個(gè)事件的產(chǎn)生乙漓,這種想法是非常錯(cuò)誤的。
事件的起點(diǎn)是由Down事件開始的释移,然后產(chǎn)生一系列的Move事件叭披,最后通常以Up事件結(jié)束。當(dāng)Down事件產(chǎn)生的時(shí)候秀鞭,會(huì)由父控件傳遞給子控件趋观,Move事件也是由父控件傳遞給子控件,Up事件也是由父控件傳遞給子控件锋边。他們都遵循同樣的傳遞事件的邏輯流程皱坛。不過Down事件最終響應(yīng)的結(jié)果,會(huì)影響到后續(xù)事件的執(zhí)行豆巨。這句話是什么意思呢剩辟?
我們看圖6,如果Down事件傳遞到了子View上往扔,但是子View的onTouchEvent對于這個(gè)Down事件的處理是return了一個(gè)false贩猎,這樣造成的結(jié)果就是會(huì)造成父View的onTouchEvent的調(diào)用,同時(shí)還有另外一個(gè)后果萍膛,那就是后續(xù)的Move事件吭服、Up事件就都傳遞不到子View上了。所以蝗罗,如果一個(gè)View要處理滑動(dòng)事件艇棕,也就是Move事件的話,那么它一定不能在onTouchEvent中串塑,對Down事件return****false.
繼續(xù)看圖6沼琉,如果Down事件到了父View上,父View需要調(diào)用自身的onInterceptTouchEvent判斷是否對這個(gè)Down事件進(jìn)行攔截桩匪,如果攔截打瘪,return了true,那么這個(gè)事件就會(huì)到父View的onTouchEvent中進(jìn)行響應(yīng)傻昙。如果此時(shí)父View的onTouchEvent也返回了true闺骚,那么代表這個(gè)父View響應(yīng)了Down事件。不過這里有一點(diǎn)不太一樣的地方是在于妆档,事件傳遞到父View的onTouchEvent方法是因?yàn)樽陨淼膐nInterceptTouchEvent方法判斷攔截導(dǎo)致的僻爽,而不是由子View回傳回來的,在這種情況下过吻,當(dāng)Move事件进泼、Up事件傳遞到父View的時(shí)候,它當(dāng)然不會(huì)傳遞給子View纤虽,并且乳绕,也不再調(diào)用自身的onInterceptTouchEvent方法了,而是dispatchTouchEventàonTouchEvent的傳遞逼纸。但是洋措,對于ViewGroupA來說,它依然是dispatchTouchàonInterceptTouchEvent的流程杰刽。
理解事件傳遞的基本邏輯菠发,對于工作過程中解決滑動(dòng)事件沖突是非常有幫助的。比如我們此時(shí)有一個(gè)父控件ViewPager贺嫂,這個(gè)ViewPager的其中一個(gè)Item是ScrollView.此時(shí)會(huì)發(fā)生什么問題呢滓鸠?當(dāng)ViewPager滑動(dòng)到ScrollView這個(gè)條目的時(shí)候,再左右滑動(dòng)第喳,發(fā)現(xiàn)ViewPager再也左右滑動(dòng)不了了糜俗。這是為什么呢?我們結(jié)合圖6一起來分析一下曲饱。
1悠抹、 我們都知道ViewPager是能夠橫向滑動(dòng)的控件,而ScrollView是縱向滑動(dòng)的控件扩淀,當(dāng)Down事件產(chǎn)生的時(shí)候楔敌,此時(shí)會(huì)由ViewPager傳遞給ScrollView,ViewPager沒有對Down事件攔截驻谆,ScrollView也不會(huì)對這個(gè)Down事件進(jìn)行攔截卵凑,所以事件就會(huì)傳遞給ScrollView的孩子,也就是類似于圖6中的子View旺韭,子View如果沒有對Down事件響應(yīng)氛谜,那么最后會(huì)到ScrollView中的onTouchEvent,而ScrollView的onTouchEvent對于這個(gè)Down事件返回了true区端,代表ScrollView消費(fèi)了這個(gè)Down事件值漫。
2、 接下來開始滑動(dòng)手指织盼,產(chǎn)生一系列的Move事件杨何。Move事件也是由ViewPager傳遞給ScrollView。由于Down事件是被ScrollView的onTouchEvent中消費(fèi)的沥邻,所以Move事件就不會(huì)傳遞給ScrollView的子控件了危虱。一系列的Move事件也是在ScrollView的onTouchEvent中被執(zhí)行。
3唐全、 最后的Up事件也是由ScrollView中的onTouchEvent消費(fèi)
從上述的1埃跷、2蕊玷、3個(gè)步驟我們看出來無論是Down事件、Move事件還是Up事件弥雹,最后全部都是被ScrollView所消費(fèi)垃帅。從頭到尾ViewPager的onTouchEvent都沒有得到執(zhí)行。而ViewPager之所以能夠左右滑動(dòng)剪勿,正是因?yàn)閂iewPager的onTouchEvent里面的代碼邏輯產(chǎn)生的效果贸诚。ViewPager的onTouchEvent沒有執(zhí)行,這個(gè)ViewPager當(dāng)然就不能夠左右滑動(dòng)了厕吉。所以解決上述問題酱固,就是在于如何讓ViewPager中的onTouchEvent方法執(zhí)行。
我們可以自定義一個(gè)MyViewPager繼承ViewPager头朱,重寫onInterceptTouchEvent方法运悲,如果我們在onInterceptTouchEvent方法中直接野蠻的return 一個(gè)true,此時(shí)就是代表無論是Down事件项钮,還是Move事件扇苞,還是Up事件,全部都攔截下來了寄纵,攔截在MyViewPager中鳖敷,我們可以認(rèn)為是圖6中的ViewGroupB,既然攔截下來了所有事件程拭,那么所有事件就會(huì)傳遞到MyViewPager的onTouchEvent定踱,所以此時(shí),這個(gè)MyViewPager一定可以左右滑動(dòng)恃鞋。
但是崖媚,由此會(huì)引發(fā)另外一個(gè)問題,就是這個(gè)ScrollView不能上下滑動(dòng)了恤浪。這又是為什么呢畅哑?因?yàn)镾crollView能夠上下滑動(dòng)的代碼邏輯在ScrollView中的onTouchEvent方法內(nèi),而此時(shí)事件由全部被MyViewPager攔截下來了水由,ScrollView完全得不到事件荠呐,onTouchEvent方法得不到執(zhí)行,自然不能上下滑動(dòng)砂客。所以我們需要修改MyViewPager中的onInterceptTouchEvent的邏輯泥张。
ViewPager只對左右滑動(dòng)感興趣,而ScrollView對上下滑動(dòng)這個(gè)動(dòng)作感興趣鞠值,所以我們只需要在MyViewPager的onInterceptTouchEvent中媚创,根據(jù)多個(gè)Move事件,判斷是左右滑動(dòng)還是上下滑動(dòng)彤恶,左右滑動(dòng)的話钞钙,return true將事件攔截下來鳄橘,上下滑動(dòng)的話,return false將事件傳遞給ScrollView芒炼,這樣就能解決問題了挥唠。
所以,對于Down事件焕议,我們一般都不進(jìn)行攔截,判斷是否攔截得根據(jù)一些列的Move事件才能得出具體的條件是否成立弧关。
Cancel事件的產(chǎn)生:
剛才我們說了事件一般有三個(gè)盅安,Down、Move世囊、Up别瞭,這三個(gè)事件比較好理解。其實(shí)還有一種事件就是Cancel事件株憾。它代表什么含義呢蝙寨?
還是回到圖6,如果一個(gè)Down事件產(chǎn)生了嗤瞎,這個(gè)Down事件從ViewGroupA傳遞到ViewGroupB最終到達(dá)子View墙歪,被子View的onTouchEvent消費(fèi),return了true贝奇,那么此時(shí)Down事件就終止了虹菲。接下來后續(xù)的Move事件也會(huì)從ViewGroupA傳遞給ViewGroupB,也就是說ViewGroupA和ViewGroupB會(huì)比子View更先拿到Move事件掉瞳,那既然ViewGroupA和ViewGroupB比子View更先拿到Move事件毕源,那么他們當(dāng)中的任何一個(gè)都有可能在某一個(gè)Move事件中,把這個(gè)Move事件給攔截下來陕习,一旦Move事件被攔截下來了霎褐,子View肯定就拿不到這個(gè)Move事件了,不過该镣,此時(shí)子View會(huì)產(chǎn)生一個(gè)新的事件冻璃,就是Cancel事件。
所以一個(gè)正常的事件序列是 DownàMoveàUp,這樣才被認(rèn)為是一個(gè)正常的事件序列损合。如果一個(gè)View響應(yīng)的Down事件俱饿,可是卻被沒有正常結(jié)尾,Move事件或者Up事件被攔截了塌忽,此時(shí)非正常結(jié)尾的情況就會(huì)給子View產(chǎn)生一個(gè)新的事件Cancel
子控件可以影響父控件是否攔截的行為
子控件是可以干預(yù)父控件是否攔截事件的結(jié)果的拍埠。通過在子View中dispatchTouchEvent中增加一行代碼即可。getParent().requestDisallowInterceptTouchEvent(true);這行代碼就可以請求父控件不要攔截事件土居。
很多人可能不太明白這句話的意思枣购,既然事件一定是先到達(dá)父控件的嬉探,然后才到達(dá)子View的,那也就是getParent().requestDisallowInterceptTouchEvent(true);這句話是在父控件是否攔截判斷結(jié)束之后棉圈,才調(diào)用涩堤,怎么能改變父控件是否攔截的結(jié)果呢,這里存在一個(gè)執(zhí)行先后順序的疑惑分瘾。
其實(shí)是這樣的胎围,getParent().requestDisallowInterceptTouchEvent(true);達(dá)到的效果不是修改父控件對本次事件是否攔截的結(jié)果。而影響的是后續(xù)事件德召。比如子View在Down事件中調(diào)用了getParent().requestDisallowInterceptTouchEvent(true);這行代碼白魂,那么在后續(xù)Move事件、Up事件產(chǎn)生到達(dá)父控件的時(shí)候上岗,父控件就不會(huì)再攔截了福荸。所以getParent().requestDisallowInterceptTouchEvent(true);只會(huì)影響Move事件和Up事件,是影響不到Down事件的肴掷。
事件模型2:同一個(gè)View中
在這種事件模型中敬锐,主要是要理清楚點(diǎn)擊事件和觸摸事件的邏輯關(guān)系。
我們先來回顧一下我們怎么設(shè)置點(diǎn)擊事件和觸摸事件的呆瞻。點(diǎn)擊事件是響應(yīng)了onClick方法台夺,而觸摸事件是響應(yīng)了onTouch方法,那他們兩者是處于一種怎么樣的關(guān)系呢痴脾?通過事件模型1的結(jié)論我們知道谒养,如果事件到達(dá)了一個(gè)View上,那么最先調(diào)用這個(gè)View的dispatchTouchEvent明郭,我們就來看看View的dispatchTouchEvent
1 public boolean dispatchTouchEvent(MotionEvent event) {
2 //...省略代碼...
3 ListenerInfo li = mListenerInfo;
4 if (li != null && li.mOnTouchListener != null
5 && (mViewFlags & ENABLED_MASK) == ENABLED
6 && li.mOnTouchListener.onTouch(this, event)) {
7 return true;
8 }
9 if (onTouchEvent(event)) {
10 return true;
11 }
12 //...省略代碼
核心代碼如上所示买窟。通過上述代碼第9行我們看出了在View中的dispatchTouchEvent中直接調(diào)用了onTouchEvent方法,但是注意薯定,在onTouchEvent方法調(diào)用之前始绍,還做了一件事情,那就是代碼第4行一直到第7行的邏輯话侄。
第4行的意思是判斷mListenerInfo是否為空和mListenerInfo中的mOnTouchEventListener是否為空亏推。只要你給這個(gè)View設(shè)置了setOnTouchListener(onTouchListener),那么這兩個(gè)值都不會(huì)為空年堆。
第5行的意思判斷一下這個(gè)View是否是Enable的狀態(tài)吞杭。
第6行的意思是調(diào)用mOnTouchListener中的onTouch方法,而這個(gè)mOnTouchListener就是你給這個(gè)View設(shè)置的觸摸監(jiān)聽對象变丧,如果這個(gè)onTouch方法返回了true芽狗,那么所有的if條件成立,進(jìn)入到第7行的代碼邏輯痒蓬,直接return 了true童擎,后續(xù)的代碼全部都不走了滴劲,也就是onTouchEvent方法不走了。
這里有必要理清楚兩個(gè)方法顾复,一個(gè)是onTouch方法班挖,一個(gè)是onTouchEvent方法,很多初學(xué)者總是被這兩個(gè)方法弄的頭暈芯砸。onTouchEvent這個(gè)方法是這個(gè)View的方法萧芙,而onTouch方法是設(shè)置給這個(gè)View的觸摸監(jiān)聽對象中的方法,這兩個(gè)方法是完全不同的兩個(gè)東西假丧。
通過上述代碼我們可以得出一個(gè)結(jié)論双揪,觸摸監(jiān)聽對象的onTouch方法比View的onTouchEvent執(zhí)行的早,并且虎谢,如果觸摸監(jiān)聽對象的onTouch方法的返回值為true的話,這個(gè)View的onTouchEvent將得不到執(zhí)行曹质。
而我們經(jīng)常說的點(diǎn)擊事件onClick的執(zhí)行的代碼邏輯其實(shí)全部都是在View的onTouchEvent中婴噩,我們可以看下View中onTouchEvent的核心代碼:
1 public boolean onTouchEvent(MotionEvent event) {
2 //...省略代碼
3 if (((viewFlags & CLICKABLE) == CLICKABLE ||
4 (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {
5 switch (event.getAction()) {
6 case MotionEvent.ACTION_UP:
7 //...省略代碼
8 performClick();
9 //...省略代碼
10 break;
看代碼第8行,調(diào)用了一個(gè)方法performClick,而performClick的代碼如下:
1 public boolean performClick() {
2 ListenerInfo li = mListenerInfo;
3 if (li != null && li.mOnClickListener != null) {
4 playSoundEffect(SoundEffectConstants.CLICK);
5 li.mOnClickListener.onClick(this);
6 return true;
7 }
8 return false;
}
看代碼的第5行羽德,這里做的事情就是調(diào)用點(diǎn)擊監(jiān)聽對象中的onClick方法几莽,所以我們經(jīng)常給一個(gè)View.setOnClickListener設(shè)置點(diǎn)擊事件的監(jiān)聽,監(jiān)聽對象的onClick方法是在這個(gè)View的Up事件到來的時(shí)候進(jìn)行調(diào)用的宅静。
所以正常的調(diào)用順序是:
dispatchTouchEventà觸摸監(jiān)聽對象的onTouchàonTouchEventà點(diǎn)擊監(jiān)聽對象的onClick章蚣,
所以當(dāng)你需要響應(yīng)這個(gè)View的點(diǎn)擊事件的時(shí)候,切記不要在這個(gè)View的觸摸監(jiān)聽對象的onTouch方法中return true
還有一種很容易犯的錯(cuò)誤會(huì)造成點(diǎn)擊事件得不到響應(yīng)姨夹,比如我自定義一個(gè)View叫做MyView纤垂,重寫它的onTouchEvent方法,在onTouchEvent中return true磷账,如下:
public class MyView extends View {
public MyView(Context context) {
super(context);
}
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
return true;
}
}
這樣子之后峭沦,你就算給這個(gè)View設(shè)置了點(diǎn)擊事件也永遠(yuǎn)無效。為什么呢逃糟?按照剛才所說的邏輯吼鱼,點(diǎn)擊事件的響應(yīng)是UP事件來到時(shí)在這個(gè)View的onTouchEvent方法中做的響應(yīng)。那么這一部分的邏輯是在MyView的父類绰咽,也就是View的onTouchEvent中菇肃,注意,此時(shí)說的是父類取募,不是父控件琐谤。所以只有在MyView的onTouchEvent中調(diào)用一下super.onTouchEvent才有可能讓點(diǎn)擊事件得到響應(yīng)吧!
引申一個(gè)問題玩敏,如果我在MyView的onTouchEvent中直接return false笑跛,會(huì)怎么樣呢付魔?
這個(gè)就要牽扯到事件模型1中的知識(shí)點(diǎn)了,如果一個(gè)View在onTouchEvent對Down事件的響應(yīng)是return false飞蹂,那么后續(xù)的Move几苍、Up事件,父控件都不會(huì)再傳給這個(gè)View了陈哑。
Up事件都傳遞不到這個(gè)View上妻坝,那這個(gè)View肯定也響應(yīng)不了點(diǎn)擊事件了。
事件模型3:同一層級(jí)View
這種事件模型比較簡單惊窖,我們簡單講講即可刽宪。一個(gè)父控件包含了三個(gè)子View,這三個(gè)子View有重疊的部分界酒。如下圖所示
圖7
如果手指觸摸到藍(lán)色重疊區(qū)域圣拄,那么ViewA和ViewB和ViewC誰先拿到事件呢?
究竟是哪個(gè)View先拿到事件毁欣,得看布局文件中擺放這三個(gè)View的順序庇谆。
<ViewGroup>
<ViewA/>
<ViewB/>
<ViewC/>
</ViewGroup>
如果是這種布局類型的話,那么很明顯ViewC會(huì)先拿到事件凭疮,如果ViewC不響應(yīng)事件饭耳,ViewB才會(huì)拿到事件,如果ViewB也不響應(yīng)事件执解,那么將是ViewA拿到事件寞肖。這個(gè)其實(shí)很好理解,ViewC在布局文件的最后面衰腌,相當(dāng)于位于整個(gè)布局的最頂層新蟆,壓在了ViewB和ViewA上,按照正常的思維右蕊,肯定是ViewC先拿到事件栅葡。注意,此時(shí)事件順序ViewC—ViewB—ViewA和事件模型1沒有關(guān)系尤泽,事件模型1是父控件與子控件的傳遞欣簇。
如果想通過源碼來分析這個(gè)結(jié)論的話,其實(shí)也比較簡單坯约。當(dāng)系統(tǒng)在加載這個(gè)布局文件的時(shí)候熊咽,其實(shí)會(huì)做類似的代碼邏輯:
ViewGroup.addView(ViewA);
ViewGroup.addView(ViewB);
ViewGroup.addView(ViewC);
ViewC是最后添加的。所以當(dāng)我們調(diào)用ViewGroup.getChildAt(0)的時(shí)候闹丐,此時(shí)獲得到的是ViewA横殴,ViewGroup.getChildAt(1)àViewB,ViewGroup.getChildAt(2)àViewC
接下來我們來看看ViewGroup是怎么分發(fā)事件的。找到ViewGroup的dispatchTouchEvent方法。如下:
@Override
1 public boolean dispatchTouchEvent(MotionEvent ev) {
2 //...省略代碼
3 //先判斷一下自己是否攔截事件
4 intercepted = onInterceptTouchEvent(ev);
5 //..省略代碼
6 if (!canceled && !intercepted) {//自己不攔截衫仑,才把事件傳遞給子控件
7 //...省略代碼
8 // Find a child that can receive the event.
9 // Scan children from front to back.
10 final View[] children = mChildren;
11 //...省略代碼
12 for (int i = childrenCount - 1; i >= 0; i--) { //遍歷循環(huán)查找能響應(yīng)的子控件
13 final View child = children[childIndex];
14 if (!canViewReceivePointerEvents(child)
15 || !isTransformedTouchPointInView(x, y, child, null)) {
16 continue;
17 }
這段代碼的大體意思是對事件進(jìn)行分發(fā)梨与。先判斷一下自己是否需要攔截事件,如果自己不攔截事件再把事件分發(fā)給子控件文狱。它會(huì)把自己所有的子控件照出來粥鞋,做一次循環(huán)遍歷。通過代碼第12行到17行我們可以得出結(jié)論瞄崇,查找能夠響應(yīng)這個(gè)事件的子控件的順序是從后往前找呻粹,因?yàn)閕是從最大值開始循環(huán)的。
這個(gè)文檔主要介紹了事件的3種模型苏研,不同模型之間設(shè)計(jì)到不同的知識(shí)點(diǎn)等浊,模型與模型之間的知識(shí)點(diǎn)也存在很多關(guān)聯(lián)性。關(guān)于這一部分的知識(shí)需要同學(xué)們細(xì)細(xì)體會(huì)摹蘑。