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

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

    當(dāng)用戶點(diǎn)擊時(shí)先調(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:

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

onUserLeaveHint:

作為Activity的生命周期回調(diào)的部分,會(huì)在用戶決定將Acitivity放到后臺(tái)時(shí)被調(diào)用。例如:當(dāng)用戶按下Home鍵,onUserLeaveHint就會(huì)被調(diào)用。但是當(dāng)來電話時(shí),來電界面會(huì)自動(dòng)彈出,onUserLeaveHint就不會(huì)被調(diào)用。當(dāng)該方法被調(diào)用時(shí),他會(huì)恰好在onPause調(diào)用之前。

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

image.png

2.PhoneWindow.superDispatchTouchEvent(MotionEvent event)

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

mDecor這個(gè)就是DecorView,布局最頂層,也就是說明這個(gè)事件分發(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ā)時(shí),都需調(diào)用onInterceptTouchEvent()詢問是否攔截事件
  // a. 若在onInterceptTouchEvent()中返回false(即不攔截事件),就會(huì)讓第二個(gè)值為true,從而進(jìn)入到條件判斷的內(nèi)部
   // b. 若在onInterceptTouchEvent()中返回true(即攔截事件),就會(huì)讓第二個(gè)值為false,從而跳出了這個(gè)條件判斷
                    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),遍歷了當(dāng)前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.判斷是否點(diǎn)擊的區(qū)域是否在子View范圍類
               if (!canViewReceivePointerEvents(child)|| !isTransformedTouchPointInView(x, y, child, null)) {
                                ev.setTargetAccessibilityFocus(false);
                                continue;
                            }
                    }
            }
          // 若點(diǎn)擊的是空白處(即無任何View接收事件) / 攔截事件(手動(dòng)復(fù)onInterceptTouchEvent()//從而讓其返回true)   
         // 調(diào)用ViewGroup父類的dispatchTouchEvent(),即View.dispatchTouchEvent()
         // 因此會(huì)執(zhí)行ViewGroup的onTouch() ->> onTouchEvent() ->> performClick() ->>   //onClick(),即自己處理該事件,事件不會(huì)往下傳遞(具體請(qǐng)參考View事件的分發(fā)機(jī)制中的  //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ā)時(shí),都需調(diào)用onInterceptTouchEvent()詢問是否攔截事件
a. 若在onInterceptTouchEvent()中返回false(即不攔截事件),從而分發(fā)給子類
b. 若在onInterceptTouchEvent()中返回true(即攔截事件),從而自己處理不會(huì)分發(fā)給子類
2.通過for循環(huán),遍歷了當(dāng)前ViewGroup下的所有子View
3.判斷是否點(diǎn)擊的區(qū)域是否在子View范圍類
若點(diǎn)擊的是空白處(即無任何View接收事件)攔截事件(手動(dòng)復(fù)onInterceptTouchEvent(從而讓其返回true)
調(diào)用ViewGroup父類的dispatchTouchEvent(),即View.dispatchTouchEvent()
因此會(huì)執(zhí)行ViewGroup的onTouch() ->> onTouchEvent() ->> performClick() ->> onClick(),即自己處理該事件,事件不會(huì)往下傳遞

總結(jié)流程圖
image.png
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容