- 一個事件時事件分發(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ā)下去
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()励背,即自己處理該事件,事件不會往下傳遞