Android開發(fā)中View的事件分發(fā)探秘

View是Android開發(fā)中所有控件的基類翔冀,雖然它不屬于四大組件导街,但是它在android開發(fā)中的地位絲毫不亞于四大組件,甚至于毫不夸張的說纤子,它的重要性和使用場(chǎng)景是超出BroadcastReceiver和ContentProvider搬瑰。在日常的開發(fā)中跟view打交道的時(shí)候太多了,可以說控硼,android開發(fā)中最終的頁面呈現(xiàn)全靠view泽论。而我們?cè)陂_發(fā)中也碰到使用view給我們帶來的問題和困擾。其中一個(gè)很典型和突出的問題(view滑動(dòng)沖突)就是由于view的事件分發(fā)處理不當(dāng)導(dǎo)致的卡乾,所以深刻理解android開發(fā)中view的分發(fā)機(jī)制對(duì)于解決這一系列的問題很有必要翼悴。

觸摸事件(MotionEvent)的傳遞規(guī)則

所謂的觸摸事件的分發(fā),其實(shí)就是對(duì)用戶操作view幔妨,android系統(tǒng)做出反應(yīng)——產(chǎn)生MotionEvent事件鹦赎,系統(tǒng)對(duì)這個(gè)事件從頂級(jí)view一層一層的進(jìn)行分發(fā),直到一個(gè)具體的view接收和消化并最終響應(yīng)了該事件误堡。(當(dāng)然這里說的view可能不太準(zhǔn)確古话,因?yàn)檫@個(gè)事件可能分發(fā)到最后,整個(gè)事件鏈中的所有view都沒有接收或者消化并響應(yīng)該事件锁施,它最終會(huì)被所在的activit所消化陪踩。ps:整個(gè)原理我們會(huì)在后面做具體的解釋說明)

在整個(gè)事件鏈分發(fā)過程中,我們主要關(guān)注三個(gè)方法:

public boolean dispatchTouchEvent(MotionEvent ev);

public boolean onInterceptTouchEvent(MotionEvent ev);

public boolean onTouchEvent(MotionEvent ev);

  • dispatchTouchEvent()方法主要處理整個(gè)事件序列的分發(fā),返回值標(biāo)識(shí)當(dāng)前view是否消耗當(dāng)前傳遞事件沾谜。返回值主要受當(dāng)前view的touchEvent()和下級(jí)view的dispatchTouchEvent()影響(ps:原理稍后進(jìn)行說明)

  • onInterceptTouchEvent()方法主要用來標(biāo)識(shí)(告知)系統(tǒng)膊毁,攔截整個(gè)事件序列的view是當(dāng)前view,該方法在整個(gè)事件序列中只會(huì)有一次為true的返回值基跑,并且如果當(dāng)前view返回了true婚温,那么在該次事件序列傳遞過程中,其子view的這個(gè)方法都不會(huì)再被調(diào)用媳否。

  • onTouchEvent()方法用來處理觸摸事件栅螟,也就是該次事件序列中最終消費(fèi)事件的方法荆秦。當(dāng)然他它可能會(huì)是整個(gè)事件序列傳遞中的任意一個(gè)view消費(fèi)該次事件,當(dāng)然這個(gè)事件只能被唯一一個(gè)view(或者window力图,或者activity)所消費(fèi)

上面說的一大串可能很多人就會(huì)懵了步绸,沒關(guān)系,我們來看一段從源碼里剝離出來的偽代碼就可以把這三個(gè)方法的關(guān)系和調(diào)用捋清楚:

public boolean dispatchTouchEvent(MotionEvent ev){
  boolean consume = false;
  if (onInterceptTouchEvent(ev)) {
      consume = onTouchEvent();
  }else{
    consume = child.dispatchTouchEvent(ev);
  }
  return consume;
}

通過以上的講述吃媒,我想大家可能會(huì)對(duì)觸摸事件的傳遞規(guī)則有了一定的認(rèn)識(shí)瓤介。接下來我們稍微總結(jié)一下。

對(duì)于一個(gè)View或者一個(gè)ViewGroup而言赘那,如果事件傳遞到它刑桑,這時(shí)它的dispatchTouchEvent()方法會(huì)被調(diào)用,在dispatchTouchEvent()方法會(huì)首先調(diào)用自己的onInterceptTouchEvent()判斷是否攔截這次事件

  • 如果onInterceptTouchEvent()返回true則表示攔截該事件募舟,并且它的onTouchEvent()方法會(huì)被調(diào)用意味著事件自己交給自己處理(ps:如果onTouchEvent()也同樣返回false祠斧,那么結(jié)果會(huì)如何呢?)

  • 反之拱礁,如果onInterceptTouchEvent()返回false琢锋,那么這個(gè)事件就傳遞給它的子View(當(dāng)然傳遞給子View的前提是它得擁有子View,那么如果當(dāng)前View沒有子View呢灶,那么結(jié)果又會(huì)怎么樣呢吴超?)

  • 以之上兩條規(guī)則作為傳遞規(guī)則,直至事件被最終處理掉

觸摸事件在Activity的傳遞規(guī)則

當(dāng)用戶觸摸了頁面時(shí)填抬,系統(tǒng)會(huì)產(chǎn)生一個(gè)MotionEvent事件烛芬,該事件系統(tǒng)的一系列處理,在頁面展示層面而言飒责,它會(huì)被首先傳遞到用戶操作的Activity(當(dāng)然也有可能是Service)赘娄。之后這個(gè)事件的傳遞順序如下:

  • Activity -> Window -> 頂級(jí)View(ViewGroup)從頂級(jí)View以下就按照前文所說的事件分發(fā)機(jī)制去分發(fā)處理。

如果傳遞的過程中所有的View和window都不事件進(jìn)行響應(yīng)宏蛉,那么最終事件會(huì)一級(jí)一級(jí)的往上送遣臼,如果所有的View和Window的onTouchEvent()都返回false,那么這個(gè)事件會(huì)被送回Activity進(jìn)行處理(Activity的onTouchEvent()方法會(huì)被調(diào)用)拾并。這個(gè)用現(xiàn)實(shí)中的例子來描述的話揍堰,我給它稱之為“搞不定找老大”。

到這里為止嗅义,我們可以得出這樣一個(gè)結(jié)論:

一次事件必然需要一個(gè)處理者屏歹,實(shí)在沒有處理者和消耗者,那么它會(huì)被回傳給大Boss(Activity)之碗。如果一個(gè)View的onTouchEvent()方法被執(zhí)行了蝙眶,那么意味著這個(gè)事件序列在正常情況下是不會(huì)再往下傳遞的了,如果當(dāng)前view的onTouchEvent()返回了false褪那,那么這個(gè)事件就會(huì)被扔給上級(jí)進(jìn)行處理幽纷。

這個(gè)結(jié)論可以解決上文所提的兩個(gè)問題式塌。

onTouchEvent()、onTouch()友浸、OnClick()的區(qū)別與聯(lián)系

在這邊我就直接給出我測(cè)試的結(jié)論了峰尝,基于這一塊的源碼,感興趣的朋友可以自行查閱收恢、了解武学。

當(dāng)一個(gè)View被設(shè)置了OnTouchListener時(shí),那么如果事件傳遞到了這個(gè)View派诬,OnTouchListener的onTouch()方法會(huì)被首先調(diào)用劳淆,如果事件在onTouch()方法中被消耗掉了(返回值為true)。那就沒有onTouchEvent()方法什么事了默赂。只有onTouch()方法返回false時(shí),onTouchEvent()方法才會(huì)被執(zhí)行括勺,在onTouchEvent()方法中缆八,如果
設(shè)置了OnClickListener,這個(gè)時(shí)候會(huì)去回調(diào)OnClick()方法疾捍。也就是說奈辰,我們平時(shí)常用的OnClickListener優(yōu)先級(jí)最低,
處于事件傳遞的末端乱豆。

我事件我做主

在前文我們所講述事件傳遞規(guī)則有一條主線是不變的奖恰,事件的傳遞過程是由外向內(nèi)的,也就是從父元素往子元素一級(jí)級(jí)傳遞宛裕。這樣的話子元素如果跟父元素都能處理事件時(shí)瑟啃,按照正常事件分發(fā)流程,子元素是別想了揩尸。但是這樣不行啊蛹屿,子元素處理的必要性如果很強(qiáng),那么這個(gè)事件我們就應(yīng)該從父元素?fù)屨歼^來岩榆。那么通過requestDisallowInterceptTouchEvent() 方法可以實(shí)現(xiàn)對(duì)父元素的事件分發(fā)過程進(jìn)行干預(yù)错负,從而實(shí)現(xiàn)子元素必要時(shí)可以搶占事件的處理和消耗權(quán)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末勇边,一起剝皮案震驚了整個(gè)濱河市犹撒,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌粒褒,老刑警劉巖识颊,帶你破解...
    沈念sama閱讀 211,348評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異怀浆,居然都是意外死亡谊囚,警方通過查閱死者的電腦和手機(jī)怕享,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,122評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來镰踏,“玉大人函筋,你說我怎么就攤上這事〉煳保” “怎么了跌帐?”我有些...
    開封第一講書人閱讀 156,936評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)绊率。 經(jīng)常有香客問我谨敛,道長(zhǎng),這世上最難降的妖魔是什么滤否? 我笑而不...
    開封第一講書人閱讀 56,427評(píng)論 1 283
  • 正文 為了忘掉前任脸狸,我火速辦了婚禮,結(jié)果婚禮上藐俺,老公的妹妹穿的比我還像新娘炊甲。我一直安慰自己,他們只是感情好欲芹,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,467評(píng)論 6 385
  • 文/花漫 我一把揭開白布卿啡。 她就那樣靜靜地躺著,像睡著了一般菱父。 火紅的嫁衣襯著肌膚如雪颈娜。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,785評(píng)論 1 290
  • 那天浙宜,我揣著相機(jī)與錄音官辽,去河邊找鬼。 笑死梆奈,一個(gè)胖子當(dāng)著我的面吹牛野崇,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播亩钟,決...
    沈念sama閱讀 38,931評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼乓梨,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了清酥?” 一聲冷哼從身側(cè)響起扶镀,我...
    開封第一講書人閱讀 37,696評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎焰轻,沒想到半個(gè)月后臭觉,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,141評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,483評(píng)論 2 327
  • 正文 我和宋清朗相戀三年蝠筑,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了狞膘。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,625評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡什乙,死狀恐怖挽封,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情臣镣,我是刑警寧澤辅愿,帶...
    沈念sama閱讀 34,291評(píng)論 4 329
  • 正文 年R本政府宣布,位于F島的核電站忆某,受9級(jí)特大地震影響点待,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜弃舒,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,892評(píng)論 3 312
  • 文/蒙蒙 一癞埠、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧聋呢,春花似錦燕差、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽瓦呼。三九已至喂窟,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間央串,已是汗流浹背磨澡。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留质和,地道東北人稳摄。 一個(gè)月前我還...
    沈念sama閱讀 46,324評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像饲宿,于是被迫代替她去往敵國(guó)和親厦酬。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,492評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容