- 一個(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ā)下去

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é)流程圖



