Android事件分發(fā)機制源碼分析

  • 一個事件時事件分發(fā)是從 Activity開始的(Activity.dispatchTouchEvent())
    來自這里的圖片
    image.png
  • Android事件分發(fā)總是先傳遞到ViewGroup、再傳遞到View
    image.png

    當用戶點擊時先調(diào)用的是Activity的Activity.dispatchTouchEvent()
    image.png
源碼分析

1.Activity.dispatchTouchEvent()



 public boolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            onUserInteraction();
        }
        if (getWindow().superDispatchTouchEvent(ev)) {
            return true;
        }
        return onTouchEvent(ev);
    }

onUserInteraction:

每當Key虹脯,Touch,Trackball事件分發(fā)到當前Activity就會被調(diào)用澳厢。如果你想當你的Activity在運行的時候,能夠得知用戶正在與你的設(shè)備交互屠列,你可以override該方法没佑。
這個回調(diào)方法和onUserLeaveHint是為了幫助Activities智能的管理狀態(tài)欄Notification原叮;特別是為了幫助Activities在恰當?shù)臅r間取消Notification。
所有Activity的onUserLeaveHint 回調(diào)都會伴隨著onUserInteraction咱台。這保證當用戶相關(guān)的的操作都會被通知到络拌,例如下拉下通知欄并點擊其中的條目。
注意在Touch事件分發(fā)過程中吵护,只有Touch Down 即Touch事件的開始會觸發(fā)該回調(diào)盒音,不會在move 和 up 分發(fā)時觸發(fā)(從Activity 源碼中 dispatchTouchEvent 方法中確實是這么做的)。

onUserLeaveHint:

作為Activity的生命周期回調(diào)的部分馅而,會在用戶決定將Acitivity放到后臺時被調(diào)用祥诽。例如:當用戶按下Home鍵,onUserLeaveHint就會被調(diào)用瓮恭。但是當來電話時雄坪,來電界面會自動彈出,onUserLeaveHint就不會被調(diào)用屯蹦。當該方法被調(diào)用時维哈,他會恰好在onPause調(diào)用之前。

若將getWindow().superDispatchTouchEvent(ev)設(shè)置為ture,不會執(zhí)行下去登澜,也不會分發(fā)下去

image.png

2.PhoneWindow.superDispatchTouchEvent(MotionEvent event)

   @Override
    public boolean superDispatchTouchEvent(MotionEvent event) {
        return mDecor.superDispatchTouchEvent(event);
    }

mDecor這個就是DecorView阔挠,布局最頂層,也就是說明這個事件分發(fā)到了最頂層

3.ViewGroup.dispatchTouchEvent(MotionEvent event)
僅貼出關(guān)鍵代碼

 @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        boolean handled = false;
        if (onFilterTouchEventForSecurity(ev)) {
            final int action = ev.getAction();
            final int actionMasked = action & MotionEvent.ACTION_MASK;

            // Check for interception.
            final boolean intercepted;
            if (actionMasked == MotionEvent.ACTION_DOWN
                    || mFirstTouchTarget != null) {
                final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
                if (!disallowIntercept) {
 //1.ViewGroup每次事件分發(fā)時脑蠕,都需調(diào)用onInterceptTouchEvent()詢問是否攔截事件
  // a. 若在onInterceptTouchEvent()中返回false(即不攔截事件)购撼,就會讓第二個值為true跪削,從而進入到條件判斷的內(nèi)部
   // b. 若在onInterceptTouchEvent()中返回true(即攔截事件),就會讓第二個值為false迂求,從而跳出了這個條件判斷
                    intercepted = onInterceptTouchEvent(ev);
                    ev.setAction(action); // restore action in case it was changed
                } else {
                    intercepted = false;
                }
            } else {
                // There are no touch targets and this action is not an initial down
                // so this view group continues to intercept touches.
                intercepted = true;
            }
            TouchTarget newTouchTarget = null;
            boolean alreadyDispatchedToNewTouchTarget = false;
            // 2.通過for循環(huán)碾盐,遍歷了當前ViewGroup下的所有子View
            if (!canceled && !intercepted) {
                            if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
                                // Child wants to receive touch within its bounds.
                                mLastTouchDownTime = ev.getDownTime();
                                if (preorderedList != null) {
                                    // childIndex points into presorted list, find original index
                                    for (int j = 0; j < childrenCount; j++) {
                                        if (children[childIndex] == mChildren[j]) {
                                            mLastTouchDownIndex = j;
                                            break;
                                        }
                                    }
                                } else {
                                    mLastTouchDownIndex = childIndex;
                                }
                                mLastTouchDownX = ev.getX();
                                mLastTouchDownY = ev.getY();
                                newTouchTarget = addTouchTarget(child, idBitsToAssign);
                                alreadyDispatchedToNewTouchTarget = true;
                                break;
                            }
                            ev.setTargetAccessibilityFocus(false);
                        }
          //3.判斷是否點擊的區(qū)域是否在子View范圍類
               if (!canViewReceivePointerEvents(child)|| !isTransformedTouchPointInView(x, y, child, null)) {
                                ev.setTargetAccessibilityFocus(false);
                                continue;
                            }
                    }
            }
          // 若點擊的是空白處(即無任何View接收事件) / 攔截事件(手動復(fù)onInterceptTouchEvent()//從而讓其返回true)   
         // 調(diào)用ViewGroup父類的dispatchTouchEvent(),即View.dispatchTouchEvent()
         // 因此會執(zhí)行ViewGroup的onTouch() ->> onTouchEvent() ->> performClick() ->>   //onClick()揩局,即自己處理該事件毫玖,事件不會往下傳遞(具體請參考View事件的分發(fā)機制中的  //View.dispatchTouchEvent())
            if (mFirstTouchTarget == null) {
                // No touch targets so treat this as an ordinary view.
                handled = dispatchTransformedTouchEvent(ev, canceled, null,
                        TouchTarget.ALL_POINTER_IDS);
            } else {
                // Dispatch to touch targets, excluding the new touch target if we already
                // dispatched to it.  Cancel touch targets if necessary.
                TouchTarget predecessor = null;
                TouchTarget target = mFirstTouchTarget;
                while (target != null) {
                    final TouchTarget next = target.next;
                    if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
                        handled = true;
                    }
                }
            }
        return handled;
    }

1.ViewGroup每次事件分發(fā)時,都需調(diào)用onInterceptTouchEvent()詢問是否攔截事件
a. 若在onInterceptTouchEvent()中返回false(即不攔截事件)凌盯,從而分發(fā)給子類
b. 若在onInterceptTouchEvent()中返回true(即攔截事件)付枫,從而自己處理不會分發(fā)給子類
2.通過for循環(huán),遍歷了當前ViewGroup下的所有子View
3.判斷是否點擊的區(qū)域是否在子View范圍類
若點擊的是空白處(即無任何View接收事件)攔截事件(手動復(fù)onInterceptTouchEvent(從而讓其返回true)
調(diào)用ViewGroup父類的dispatchTouchEvent()驰怎,即View.dispatchTouchEvent()
因此會執(zhí)行ViewGroup的onTouch() ->> onTouchEvent() ->> performClick() ->> onClick()励背,即自己處理該事件,事件不會往下傳遞

總結(jié)流程圖
image.png
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末砸西,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子址儒,更是在濱河造成了極大的恐慌芹枷,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,039評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件莲趣,死亡現(xiàn)場離奇詭異鸳慈,居然都是意外死亡,警方通過查閱死者的電腦和手機喧伞,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評論 3 395
  • 文/潘曉璐 我一進店門走芋,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人潘鲫,你說我怎么就攤上這事翁逞。” “怎么了溉仑?”我有些...
    開封第一講書人閱讀 165,417評論 0 356
  • 文/不壞的土叔 我叫張陵挖函,是天一觀的道長。 經(jīng)常有香客問我浊竟,道長怨喘,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,868評論 1 295
  • 正文 為了忘掉前任振定,我火速辦了婚禮必怜,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘后频。我一直安慰自己梳庆,他們只是感情好,可當我...
    茶點故事閱讀 67,892評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著靠益,像睡著了一般丧肴。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上胧后,一...
    開封第一講書人閱讀 51,692評論 1 305
  • 那天芋浮,我揣著相機與錄音,去河邊找鬼壳快。 笑死纸巷,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的眶痰。 我是一名探鬼主播瘤旨,決...
    沈念sama閱讀 40,416評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼竖伯!你這毒婦竟也來了存哲?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,326評論 0 276
  • 序言:老撾萬榮一對情侶失蹤七婴,失蹤者是張志新(化名)和其女友劉穎祟偷,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體打厘,經(jīng)...
    沈念sama閱讀 45,782評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡修肠,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,957評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了户盯。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片嵌施。...
    茶點故事閱讀 40,102評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖莽鸭,靈堂內(nèi)的尸體忽然破棺而出吗伤,到底是詐尸還是另有隱情,我是刑警寧澤硫眨,帶...
    沈念sama閱讀 35,790評論 5 346
  • 正文 年R本政府宣布牲芋,位于F島的核電站,受9級特大地震影響捺球,放射性物質(zhì)發(fā)生泄漏缸浦。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,442評論 3 331
  • 文/蒙蒙 一氮兵、第九天 我趴在偏房一處隱蔽的房頂上張望裂逐。 院中可真熱鬧,春花似錦泣栈、人聲如沸卜高。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽掺涛。三九已至庭敦,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間薪缆,已是汗流浹背秧廉。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留拣帽,地道東北人疼电。 一個月前我還...
    沈念sama閱讀 48,332評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像减拭,于是被迫代替她去往敵國和親蔽豺。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,044評論 2 355

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