// 點擊事件產生後,會直接調用dispatchTouchEvent分發方法
public boolean dispatchTouchEvent(MotionEvent ev) {
//代表是否消耗事件
boolean consume = false;
if (onInterceptTouchEvent(ev)) {
//如果onInterceptTouchEvent()返回true則代表當前View攔截了點擊事件
//則該點擊事件則會交給當前View進行處理
//即調用onTouchEvent ()方法去處理點擊事件
consume = onTouchEvent (ev) ;
} else {
//如果onInterceptTouchEvent()返回false則代表當前View不攔截點擊事件
//則該點擊事件則會繼續傳遞給它的子元素
//子元素的dispatchTouchEvent()就會被調用,重複上述過程
//直到點擊事件被最終處理爲止
consume = child.dispatchTouchEvent (ev) ;
}
return consume;
}
總結
1、事件分發時由activity->viewgroup->view,依次向下傳遞,消費事件後,且事件不會往下傳遞,後續事件會繼續分發到該view
2、事件不被消費,且事件停止傳遞,將事件回傳給父控件的onTouchEvent處理,當前view仍然接受此事件的其他事件
3、攔截事件後,事件不會往下傳遞,自己處理事件,即執行自己的onTouchEvent();同事件列的其他事件都由該view處理;在同一個事件列中的onInterceptTouchEvent()該方法不會再次被調用。
4、不攔截事件,事件會繼續往下傳遞,事件傳遞到子view ,後續事件序列讓其處理
5、自己處理事件onToutchEvent(),事件不會繼續傳遞,後續事件序列讓其處理
6、不處理事件,將事件往上傳遞給父控件的onToutchEvent()處理,當前view不再接受此事件的其他事件
ViewGroup的dispatchTouchEvent()源碼分析
// 發生ACTION_DOWN事件或者已經發生過ACTION_DOWN,並且將mFirstTouchTarget賦值,才進入此區域,主要功能是攔截器
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN|| mFirstTouchTarget != null) {
//disallowIntercept:是否禁用事件攔截的功能(默認是false),即不禁用
//可以在子View通過調用requestDisallowInterceptTouchEvent方法對這個值進行修改,不讓該View攔截事件
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
//默認情況下會進入該方法
if (!disallowIntercept) {
//調用攔截方法
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action);
} else {
intercepted = false;
}
} else {
// 當沒有觸摸targets,且不是down事件時,開始持續攔截觸摸。
intercepted = true;
}
如果遍歷所有子View或ViewGroup,都沒有消費事件。ViewGroup會自己處理事件
如果子元素的dispatchTouchEvent()方法返回true,那麼mFirstTouchTarget就會被賦值,同時跳出for循環
這一段的內容主要是爲判斷是否攔截。如果當前事件的MotionEvent.ACTION_DOWN,則進入判斷,調用ViewGroup onInterceptTouchEvent()方法的值,判斷是否攔截。如果mFirstTouchTarget != null,即已經發生過MotionEvent.ACTION_DOWN,並且該事件已經有ViewGroup的子View進行處理了,那麼也進入判斷,調用ViewGroup onInterceptTouchEvent()方法的值,判斷是否攔截。如果不是以上兩種情況,即已經是MOVE或UP事件了,並且之前的事件沒有對象進行處理,則設置成true,開始攔截接下來的所有事件。 這也就解釋瞭如果子View的onTouchEvent()方法返回false,那麼接下來的一些列事件都不會交給他處理。如果VieGroup的onInterceptTouchEvent()第一次執行爲true,則mFirstTouchTarget = null,則也會使得接下來不會調用onInterceptTouchEvent(),直接將攔截設置爲true
addTouchTarget(child, idBitsToAssign);
內部完成mFirstTouchTarget被賦值。如果mFirstTouchTarget爲空,將會讓ViewGroup默認攔截所有操作
/* 從最底層的父視圖開始遍歷,
** 找尋newTouchTarget,即上面的mFirstTouchTarget
** 如果已經存在找尋newTouchTarget,說明正在接收觸摸事件,則跳出循環。
*/
for (int i = childrenCount - 1; i >= 0; i--) {
final int childIndex = customOrder
? getChildDrawingOrder(childrenCount, i) : i;
final View child = (preorderedList == null)
? children[childIndex] : preorderedList.get(childIndex);
// 如果當前視圖無法獲取用戶焦點,則跳過本次循環
if (childWithAccessibilityFocus != null) {
if (childWithAccessibilityFocus != child) {
continue;
}
childWithAccessibilityFocus = null;
i = childrenCount - 1;
}
//如果view不可見,或者觸摸的座標點不在view的範圍內,則跳過本次循環
if (!canViewReceivePointerEvents(child)
|| !isTransformedTouchPointInView(x, y, child, null)) {
ev.setTargetAccessibilityFocus(false);
continue;
}
newTouchTarget = getTouchTarget(child);
// 已經開始接收觸摸事件,並退出整個循環。
if (newTouchTarget != null) {
newTouchTarget.pointerIdBits |= idBitsToAssign;
break;
}
//重置取消或擡起標誌位
//如果觸摸位置在child的區域內,則把事件分發給子View或ViewGroup
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
// 獲取TouchDown的時間點
mLastTouchDownTime = ev.getDownTime();
// 獲取TouchDown的Index
if (preorderedList != null) {
for (int j = 0; j < childrenCount; j++) {
if (children[childIndex] == mChildren[j]) {
mLastTouchDownIndex = j;
break;
}
}
} else {
mLastTouchDownIndex = childIndex;
}
//獲取TouchDown的x,y座標
mLastTouchDownX = ev.getX();
mLastTouchDownY = ev.getY();
//添加TouchTarget,則mFirstTouchTarget != null。
newTouchTarget = addTouchTarget(child, idBitsToAssign);
//表示以及分發給NewTouchTarget
alreadyDispatchedToNewTouchTarget = true;
break;
}
詳細查看view事件分發
onTouch->onTouchEvent->onClick優先執行順序
view事件分發僞代碼