Android事件傳遞機(jī)制

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ā)的邏輯是怎樣的呢裕寨?

image.png
    圖1

A、 它首先會(huì)先調(diào)用自身的onInterceptTouchEvent方法派继,調(diào)用此方法的目的是為了先讓自己這個(gè)控件判斷下是否需要把此事件攔截下來宾袜,如果攔截下來,那么就代表自己這個(gè)控件需要來處理這個(gè)事件驾窟,所以此時(shí)會(huì)調(diào)用自身了onTouchEvent來對這個(gè)事件進(jìn)行響應(yīng)庆猫。

image.png

圖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流程的邏輯荤西。

image.png

圖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)此事件构罗。

image.png

圖4

通過ABC三個(gè)流程铜涉,我們就能夠非常清楚的清楚事件傳遞的邏輯。父控件傳遞給子控件遂唧,當(dāng)沒有控件攔截事件的話芙代,將會(huì)一層層的向下傳遞,直到最后一個(gè)子控件盖彭。

事件傳遞的流程說完之后纹烹,我們就來說一說事件響應(yīng)的方向。我們知道onTouchEvent也是有返回值的召边,要么是true铺呵,要么是false,不同的返回值會(huì)有不同的表現(xiàn)隧熙。

image.png

圖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)的方向****:子控件****à****父控件

更加清晰的圖:

image.png

圖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有重疊的部分界酒。如下圖所示

image.png

圖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ì)摹蘑。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末筹燕,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子衅鹿,更是在濱河造成了極大的恐慌撒踪,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,402評(píng)論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件塘安,死亡現(xiàn)場離奇詭異糠涛,居然都是意外死亡援奢,警方通過查閱死者的電腦和手機(jī)兼犯,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來集漾,“玉大人切黔,你說我怎么就攤上這事【咂” “怎么了纬霞?”我有些...
    開封第一講書人閱讀 162,483評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長驱显。 經(jīng)常有香客問我诗芜,道長,這世上最難降的妖魔是什么埃疫? 我笑而不...
    開封第一講書人閱讀 58,165評(píng)論 1 292
  • 正文 為了忘掉前任伏恐,我火速辦了婚禮,結(jié)果婚禮上栓霜,老公的妹妹穿的比我還像新娘翠桦。我一直安慰自己,他們只是感情好胳蛮,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評(píng)論 6 388
  • 文/花漫 我一把揭開白布销凑。 她就那樣靜靜地躺著丛晌,像睡著了一般。 火紅的嫁衣襯著肌膚如雪斗幼。 梳的紋絲不亂的頭發(fā)上澎蛛,一...
    開封第一講書人閱讀 51,146評(píng)論 1 297
  • 那天,我揣著相機(jī)與錄音孟岛,去河邊找鬼瓶竭。 笑死,一個(gè)胖子當(dāng)著我的面吹牛渠羞,可吹牛的內(nèi)容都是我干的斤贰。 我是一名探鬼主播,決...
    沈念sama閱讀 40,032評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼次询,長吁一口氣:“原來是場噩夢啊……” “哼荧恍!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起屯吊,我...
    開封第一講書人閱讀 38,896評(píng)論 0 274
  • 序言:老撾萬榮一對情侶失蹤送巡,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后盒卸,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體骗爆,經(jīng)...
    沈念sama閱讀 45,311評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評(píng)論 2 332
  • 正文 我和宋清朗相戀三年蔽介,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了摘投。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,696評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡虹蓄,死狀恐怖犀呼,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情薇组,我是刑警寧澤外臂,帶...
    沈念sama閱讀 35,413評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站律胀,受9級(jí)特大地震影響宋光,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜炭菌,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評(píng)論 3 325
  • 文/蒙蒙 一罪佳、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧娃兽,春花似錦菇民、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽阔馋。三九已至,卻和暖如春娇掏,著一層夾襖步出監(jiān)牢的瞬間呕寝,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評(píng)論 1 269
  • 我被黑心中介騙來泰國打工婴梧, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留下梢,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,698評(píng)論 2 368
  • 正文 我出身青樓塞蹭,卻偏偏與公主長得像孽江,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子番电,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評(píng)論 2 353