Android源碼之事件分發(fā)機(jī)制快速理解

整個(gè)事件分發(fā)的流程圖如下擒抛,現(xiàn)在我們根據(jù)代碼去一步步畫(huà)出整個(gè)事件分發(fā)的流程圖推汽。

image.png
事件方法 dispatchTouchEvent onInterceptTouchEvent onTouchEvent
ViewGroup 無(wú)(繼承View ,在view中可以找到)
View 無(wú)

下面代碼是抽出來(lái)的整個(gè)事件分發(fā)的核心偽代碼:

// Activity中該方法的核心部分偽代碼
1 public boolean dispatchTouchEvent(MotionEvent ev) {
2    if (child.dispatchTouchEvent(ev)) {
        //如果子View消費(fèi)了該事件,則返回TRUE歧沪,讓調(diào)用者知道該事件已被消費(fèi)
3        return true;   
4    } else {
        //如果子View沒(méi)有消費(fèi)該事件歹撒,則調(diào)用自身的onTouchEvent嘗試處理。
5        return onTouchEvent(ev);    
6    }
7}


// ViewGroup_1 中該方法的核心部分偽代碼
8 public boolean dispatchTouchEvent(MotionEvent ev) {
9    if (!onInterceptTouchEvent(ev)) {
        //不攔截诊胞,則傳給子View進(jìn)行分發(fā)處理
10        return child.dispatchTouchEvent(ev);   
11    } else {
        //攔截事件暖夭,交由自身對(duì)象的onTouchEvent方法處理
12        return onTouchEvent(ev);    
13    }
14}


// ViewGroup_2中該方法的核心部分偽代碼
15 public boolean dispatchTouchEvent(MotionEvent ev) {
16    if (!onInterceptTouchEvent(ev)) {
            //不攔截,則傳給子View進(jìn)行分發(fā)處理
17        return child.dispatchTouchEvent(ev);   
18    } else {
            //攔截事件撵孤,交由自身對(duì)象的onTouchEvent方法處理
19        return onTouchEvent(ev);   
20    }
21 }


// View中該方法的核心部分偽代碼
22 public boolean dispatchTouchEvent(MotionEvent ev) {
23    //如果該對(duì)象的監(jiān)聽(tīng)成員變量不為空鳞尔,則會(huì)調(diào)用其onTouch方法,
24    if (mOnTouchListener != null && mOnTouchListener.onTouch(this, event)) {
25        return true;    //若onTouch方法返回TRUE早直,則表示消費(fèi)了該事件寥假,則dispachtouTouchEvent返回TRUE,讓其調(diào)用者知道該事件已被消費(fèi)霞扬。
26    }
27    return onTouchEvent(ev);    //若監(jiān)聽(tīng)成員為空或onTouch沒(méi)有消費(fèi)該事件糕韧,則調(diào)用對(duì)象自身的onTouchEvent方法處理。
28 }

我們就對(duì)照流程圖和核心偽代碼來(lái)分析整個(gè)流程喻圃。然后自己可以一步步畫(huà)出流程圖
都知道當(dāng)我們按下觸摸到屏幕 事件是從驅(qū)動(dòng)的 dev/input/event[x] 開(kāi)始的萤彩。
事件一直傳遞到Activity的dispatchTouchEvent中。好了斧拍,這塊不是我們今天的重點(diǎn)雀扶。
先來(lái)看Activity中dispatchTouchEvent 事件到達(dá)這里后 如果2返回true 就表示消費(fèi)事件, flase 那么就走它自己的 onTouchEvent肆汹。
代碼就像這樣愚墓。

1.從Activity->ViewGroup
// Activity中該方法的核心部分偽代碼
1 public boolean dispatchTouchEvent(MotionEvent ev) {
2    if (child.dispatchTouchEvent(ev)) {
3        return true;    //如果子View消費(fèi)了該事件,則返回TRUE,讓調(diào)用者知道該事件已被消費(fèi)
4    } else {
5        return onTouchEvent(ev);    //如果子View沒(méi)有消費(fèi)該事件昂勉,則調(diào)用自身的onTouchEvent嘗試處理浪册。
6    }
7 }

// 重寫ViewGroup_1 中的該方法
8  public boolean dispatchTouchEvent(MotionEvent ev) {
9    //return flase;
9    //return true;
10 }

對(duì)應(yīng)的流程圖如下

image.png
2.從ViewGroup->View
//繼續(xù)看 ViewGroup_1 中該方法的核心部分偽代碼
8 public boolean dispatchTouchEvent(MotionEvent ev) {
9    if (!onInterceptTouchEvent(ev)) { //取反
10        return child.dispatchTouchEvent(ev);    //不攔截,則傳給子View進(jìn)行分發(fā)處理
11    } else {
12        return onTouchEvent(ev);    //攔截事件岗照,交由自身對(duì)象的onTouchEvent方法處理()
13    }
14}

如果onInterceptTouchEvent(ev) 返回true 那么就會(huì)走自己(12)的繼承類view的onTouchEvent(ev)村象,表示ViewGroup_1要 攔截這個(gè)事件。
如果onInterceptTouchEvent(ev) 返回false 那么就會(huì)走child.dispatchTouchEvent(ev) 攒至,ViewGroup_1 不攔截這個(gè)事件,繼續(xù)往下傳遞厚者。默認(rèn)false。

對(duì)應(yīng)的流程圖如下:
image.png
3.從View到onTouchEvent

然后到了調(diào)用到了child.dispatchTouchEvent(ev),Child = View

22 public boolean dispatchTouchEvent(MotionEvent ev) {
23    //如果該對(duì)象的監(jiān)聽(tīng)成員變量不為空迫吐,則會(huì)調(diào)用其onTouch方法库菲,
24    if (mOnTouchListener != null && mOnTouchListener.onTouch(this, event)) {
      //若onTouch方法返回TRUE,則表示消費(fèi)了該事件渠抹,則dispachtouTouchEvent返回TRUE蝙昙,讓其調(diào)用者知道該事件已被消費(fèi)闪萄。
25        return true;    
26    }
     //若監(jiān)聽(tīng)成員為空或onTouch沒(méi)有消費(fèi)該事件,則調(diào)用對(duì)象自身的onTouchEvent方法處理奇颠。
27    return onTouchEvent(ev);   
28 }

對(duì)應(yīng)的完整的流程圖如下败去。
其中如果我們不做任何干預(yù)的話流程會(huì)是:1->2->3->4->5->6->7。


image.png

流程圖畫(huà)完了烈拒,可以大腦里面再整理一下圆裕。整個(gè)流程還是很簡(jiǎn)單的 采用遞歸的思想去處理事件分發(fā)的過(guò)程。

----------------事件的調(diào)用順序--------------------

父ViewGroup dispatchTocuhEvent
父ViewGroup onInterceptionTouchEvent
子ViewGroup dispatchTocuhEvent
子ViewGroup onInterceptionTouchEvent
子 onTouchEvent
父 onTouchEvent


結(jié)論:

事件沖突的規(guī)律解決:
1.(外部攔截 “推薦的做法”)在父ViewGroup 做攔截操作:那么父ViewGroup需要重寫onInterceptionTouchEvent 做邏輯判斷攔截就 在down 返回true 荆几,并且在onTouchEvent中一定要返回true消費(fèi)此次事件吓妆,不然會(huì)收不到后續(xù)事件。
2.(內(nèi)部攔截)在子ViewGroup做攔截操作:那么子ViewGroup可以重寫onDispatchTocuhEvent 做邏輯判斷吨铸。攔截的話 就 需要調(diào)用在dispatchTocuhEvent中down中調(diào)用requestDisllowInterceptionTouchEvent(true) 才能攔截下次move之后的事件行拢,告訴父ViewGroup的onInterceptionTouchEvent 你不要攔截 那么就可以走我的onDispatchTocuhEvent 事件。
requestDisllowInterceptionTouchEvent() 是無(wú)法攔截 父ViewGroup的down事件的诞吱,因?yàn)樵贒own事件的時(shí)候ViewGroup會(huì)清除掉所有View事件的標(biāo)記舟奠。只能攔截 父ViewGroup Down之后的事件

dispatchTocuhEvent 部分代碼,對(duì)以上結(jié)論說(shuō)明:

   if (actionMasked == MotionEvent.ACTION_DOWN) {
              //down清除標(biāo)記位,然后分發(fā)事件
                cancelAndClearTouchTargets(ev);
                resetTouchState();
   }
    final boolean intercepted;
   if (actionMasked == MotionEvent.ACTION_DOWN
                    || mFirstTouchTarget != null) {
                final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
                   //FLAG_DISALLOW_INTERCEPT 房维, disallowIntercept = true
                    if (!disallowIntercept) { 
                   //如果父ViewGroup 重寫onInterceptTouchEvent 默認(rèn)返回true沼瘫,那么子VIew還想收到事件那么就不讓你進(jìn)這里來(lái)               
                    intercepted = onInterceptTouchEvent(ev);
                    ev.setAction(action);
                } else {//走這里那么子View又可以愉快的相應(yīng)down之后的事件拉
                    intercepted = false;
                }
            } else {
                intercepted = true;
     }
    
    if (!canceled && !intercepted) {
      //處理事件分發(fā)
    }

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市咙俩,隨后出現(xiàn)的幾起案子耿戚,更是在濱河造成了極大的恐慌,老刑警劉巖阿趁,帶你破解...
    沈念sama閱讀 211,265評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件梳星,死亡現(xiàn)場(chǎng)離奇詭異汗侵,居然都是意外死亡盒卸,警方通過(guò)查閱死者的電腦和手機(jī)铲敛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門赔癌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)卧惜,“玉大人攀痊,你說(shuō)我怎么就攤上這事岸霹≡晁” “怎么了纷铣?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,852評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)战转。 經(jīng)常有香客問(wèn)我搜立,道長(zhǎng),這世上最難降的妖魔是什么槐秧? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,408評(píng)論 1 283
  • 正文 為了忘掉前任啄踊,我火速辦了婚禮忧设,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘颠通。我一直安慰自己址晕,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,445評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布顿锰。 她就那樣靜靜地躺著谨垃,像睡著了一般。 火紅的嫁衣襯著肌膚如雪硼控。 梳的紋絲不亂的頭發(fā)上刘陶,一...
    開(kāi)封第一講書(shū)人閱讀 49,772評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音牢撼,去河邊找鬼匙隔。 笑死,一個(gè)胖子當(dāng)著我的面吹牛熏版,可吹牛的內(nèi)容都是我干的牡直。 我是一名探鬼主播,決...
    沈念sama閱讀 38,921評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼纳决,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼碰逸!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起阔加,我...
    開(kāi)封第一講書(shū)人閱讀 37,688評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤饵史,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后胜榔,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體胳喷,經(jīng)...
    沈念sama閱讀 44,130評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,467評(píng)論 2 325
  • 正文 我和宋清朗相戀三年夭织,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了吭露。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,617評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡尊惰,死狀恐怖讲竿,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情弄屡,我是刑警寧澤题禀,帶...
    沈念sama閱讀 34,276評(píng)論 4 329
  • 正文 年R本政府宣布,位于F島的核電站膀捷,受9級(jí)特大地震影響迈嘹,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,882評(píng)論 3 312
  • 文/蒙蒙 一秀仲、第九天 我趴在偏房一處隱蔽的房頂上張望融痛。 院中可真熱鬧,春花似錦神僵、人聲如沸雁刷。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,740評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)安券。三九已至,卻和暖如春氓英,著一層夾襖步出監(jiān)牢的瞬間侯勉,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,967評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工铝阐, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留址貌,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,315評(píng)論 2 360
  • 正文 我出身青樓徘键,卻偏偏與公主長(zhǎng)得像练对,于是被迫代替她去往敵國(guó)和親吹害。 傳聞我的和親對(duì)象是個(gè)殘疾皇子螟凭,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,486評(píng)論 2 348

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