本文是學(xué)習(xí)Android事件分發(fā)機(jī)制的學(xué)習(xí)筆記,一是為了鞏固學(xué)習(xí)成果,加深印象;二是為了方便以后查閱液荸。
Activity對(duì)事件的分發(fā)過(guò)程
從Activity#dispatchTouchEvent()
開(kāi)始看起:
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}
首先對(duì)ACTION_DOWN
事件進(jìn)行了特殊判斷,調(diào)用onUserInteraction()
,跟進(jìn)這個(gè)方法脱篙,會(huì)發(fā)現(xiàn)是一個(gè)空方法:
public void onUserInteraction() {
}
不去管它娇钱,接下來(lái)Activity
會(huì)通過(guò)getWindow()
獲得自己所屬的Window
進(jìn)行分發(fā),Window
是個(gè)抽象類(lèi)绊困,用來(lái)控制頂級(jí)View的外觀和行為策略文搂,它的唯一實(shí)現(xiàn)類(lèi)是PhoneWindow
。那么PhoneWindow
是如何處理點(diǎn)擊事件的秤朗,PhoneWindow#superDispatchTouchEvent()
如下所示:
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
return mDecor.superDispatchTouchEvent(event);
}
很簡(jiǎn)單煤蹭,直接傳遞給了mDecor
,這個(gè)mDecor
就是當(dāng)前窗口最頂層的DecorView
。
// This is the top-level view of the window, containing the window decor.
private DecorView mDecor;
跟進(jìn)DecorView#superDispatchTouchEvent()
:
public boolean superDispatchTouchEvent(MotionEvent event) {
return super.dispatchTouchEvent(event);
}
居然是調(diào)用父類(lèi)的dispatchTouchEvent()
方法硝皂,DecorView
的父類(lèi)是FrameLayout
,繼續(xù)跟進(jìn)查看常挚,發(fā)現(xiàn)FrameLayout
并沒(méi)有這個(gè)方法,那就繼續(xù)向上追稽物,FrameLayout
的父類(lèi)是ViewGroup
奄毡,也就是說(shuō),觸摸事件經(jīng)過(guò)層層傳遞贝或,最終傳遞到ViewGroup#dispatchTouchEvent()
方法,至此吼过,事件已經(jīng)傳遞到視圖的頂級(jí)View了。
ViewGroup對(duì)事件的分發(fā)過(guò)程
接下來(lái)是重頭戲了...上代碼ViewGroup#dispatchTouchEvent()
...
public boolean dispatchTouchEvent(MotionEvent ev) {
// 調(diào)試用咪奖,不去管它
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onTouchEvent(ev, 1);
}
// 輔助功能盗忱,有些用戶(hù)由于視力上、身體上羊赵、年齡上使他們不能接受語(yǔ)音或者視覺(jué)信息
// 不是重點(diǎn)趟佃,也不去管它
if (ev.isTargetAccessibilityFocus() && isAccessibilityFocusedViewOrHost()) {
ev.setTargetAccessibilityFocus(false);
}
boolean handled = false;
// onFilterTouchEventForSecurity(ev),觸摸事件安全過(guò)濾
// 具體實(shí)現(xiàn):當(dāng)窗口被遮擋慷垮,返回false,丟棄觸摸事件揍堕;未被遮擋料身,返回true
if (onFilterTouchEventForSecurity(ev)) {
// 沒(méi)有被遮擋
final int action = ev.getAction();
final int actionMasked = action & MotionEvent.ACTION_MASK;
// Handle an initial down.
if (actionMasked == MotionEvent.ACTION_DOWN) {
// 如果是Down事件,則重置所有之前保存的狀態(tài)衩茸,因?yàn)檫@是事件序列的開(kāi)始
// mFirstTouchTarget會(huì)被設(shè)為Null
cancelAndClearTouchTargets(ev);
// 重置FLAG_DISALLOW_INTERCEPT
resetTouchState();
}
// 檢測(cè)是否攔截
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
// 標(biāo)記事件不允許被攔截芹血,默認(rèn)為false
// 可以由requestDisallowInterceptTouchEvent方法來(lái)設(shè)置
// 設(shè)置為true,ViewGroup將無(wú)法攔截Down以外的點(diǎn)擊事件
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
// 調(diào)用onInterceptTouchEvent(ev)方法楞慈,詢(xún)問(wèn)自己是否要攔截事件
// ViewGroup的onInterceptTouchEvent(ev)方法默認(rèn)返回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;
}
// If intercepted, start normal event dispatch. Also if there is already
// a view that is handling the gesture, do normal event dispatch.
if (intercepted || mFirstTouchTarget != null) {
ev.setTargetAccessibilityFocus(false);
}
// 通過(guò)標(biāo)記和Action檢查Cancel幔烛,將結(jié)果賦值給局部變量canceled
final boolean canceled = resetCancelNextUpFlag(this)
|| actionMasked == MotionEvent.ACTION_CANCEL;
// split標(biāo)記是否需要將事件分發(fā)給多個(gè)子View,默認(rèn)為true
// 可通過(guò)setMotionEventSplittingEnabled()方法設(shè)置
final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
TouchTarget newTouchTarget = null;
boolean alreadyDispatchedToNewTouchTarget = false;
// 如果沒(méi)取消也沒(méi)攔截囊蓝,進(jìn)入執(zhí)行語(yǔ)句中
if (!canceled && !intercepted) {
View childWithAccessibilityFocus = ev.isTargetAccessibilityFocus()
? findChildWithAccessibilityFocus() : null;
if (actionMasked == MotionEvent.ACTION_DOWN
|| (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
final int actionIndex = ev.getActionIndex(); // always 0 for down
final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)
: TouchTarget.ALL_POINTER_IDS;
// Clean up earlier touch targets for this pointer id in case they
// have become out of sync.
removePointersFromTouchTargets(idBitsToAssign);
final int childrenCount = mChildrenCount;
// 判斷newTouchTarget為Null饿悬,且ChildrenCount不為0
if (newTouchTarget == null && childrenCount != 0) {
final float x = ev.getX(actionIndex);
final float y = ev.getY(actionIndex);
// 尋找可以處理觸摸事件的子View
// 通過(guò)buildTouchDispatchChildList()方法構(gòu)建子View的List集合preorderedList
final ArrayList<View> preorderedList = buildTouchDispatchChildList();
final boolean customOrder = preorderedList == null
&& isChildrenDrawingOrderEnabled();
final View[] children = mChildren;
// 倒序遍歷所有的子View
for (int i = childrenCount - 1; i >= 0; i--) {
final int childIndex = getAndVerifyPreorderedIndex(
childrenCount, i, customOrder);
final View child = getAndVerifyPreorderedView(
preorderedList, children, childIndex);
// If there is a view that has accessibility focus we want it
// to get the event first and if not handled we will perform a
// normal dispatch. We may do a double iteration but this is
// safer given the timeframe.
if (childWithAccessibilityFocus != null) {
if (childWithAccessibilityFocus != child) {
continue;
}
childWithAccessibilityFocus = null;
i = childrenCount - 1;
}
// 同時(shí)滿(mǎn)足兩種情況下子View可以接收事件的分發(fā)
// canViewReceivePointerEvents()方法會(huì)判斷子View是否可見(jiàn)和是否在播放動(dòng)畫(huà)
// isTransformedTouchPointInView()方法會(huì)判斷觸摸事件坐標(biāo)是否在子View內(nèi)
if (!canViewReceivePointerEvents(child)
|| !isTransformedTouchPointInView(x, y, child, null)) {
ev.setTargetAccessibilityFocus(false);
continue;
}
// 查找當(dāng)前子View是否在mFirstTouchTarget中存儲(chǔ)
// mFirstTouchTarget是一種單鏈表結(jié)構(gòu)
// 找不到則返回Null
newTouchTarget = getTouchTarget(child);
if (newTouchTarget != null) {
// newTouchTarget不為Nul,說(shuō)明已經(jīng)找到接收的View了聚霜,break跳出for循環(huán)
newTouchTarget.pointerIdBits |= idBitsToAssign;
break;
}
resetCancelNextUpFlag(child);
// 沒(méi)有跳出循環(huán)狡恬,說(shuō)明我們找到的Child并沒(méi)有在mFirstTouchTarget中
// 調(diào)用dispatchTransformedTouchEvent()方法
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();
// 將child賦值給mFirstTouchTarget
newTouchTarget = addTouchTarget(child, idBitsToAssign);
// alreadyDispatchedToNewTouchTarget賦值為true,跳出循環(huán)
alreadyDispatchedToNewTouchTarget = true;
break;
}
// The accessibility focus didn't handle the event, so clear
// the flag and do a normal dispatch to all children.
ev.setTargetAccessibilityFocus(false);
}
if (preorderedList != null) preorderedList.clear();
}
// 沒(méi)有找到可以接收事件的子View蝎宇,并且之前的mFirstTouchTarget不為空
// newTouchTarget指向了最初的mFirstTouchTarget
if (newTouchTarget == null && mFirstTouchTarget != null) {
// Did not find a child to receive the event.
// Assign the pointer to the least recently added target.
newTouchTarget = mFirstTouchTarget;
while (newTouchTarget.next != null) {
newTouchTarget = newTouchTarget.next;
}
newTouchTarget.pointerIdBits |= idBitsToAssign;
}
}
}
if (mFirstTouchTarget == null) {
// 如果mFirstTouchTarget為null
// 調(diào)用dispatchTransformedTouchEvent()方法
// 第三個(gè)參數(shù)為null弟劲,會(huì)調(diào)用super.dispatchTouchEvent()方法
// 將當(dāng)前ViewGroup當(dāng)做普通的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;
} else {
final boolean cancelChild = resetCancelNextUpFlag(target.child)
|| intercepted;
if (dispatchTransformedTouchEvent(ev, cancelChild,
target.child, target.pointerIdBits)) {
handled = true;
}
if (cancelChild) {
if (predecessor == null) {
mFirstTouchTarget = next;
} else {
predecessor.next = next;
}
target.recycle();
target = next;
continue;
}
}
predecessor = target;
target = next;
}
}
// Update list of touch targets for pointer up or cancel, if needed.
if (canceled
|| actionMasked == MotionEvent.ACTION_UP
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
resetTouchState();
} else if (split && actionMasked == MotionEvent.ACTION_POINTER_UP) {
final int actionIndex = ev.getActionIndex();
final int idBitsToRemove = 1 << ev.getPointerId(actionIndex);
// 當(dāng)某個(gè)手指抬起時(shí),清除與它相關(guān)的數(shù)據(jù)
removePointersFromTouchTargets(idBitsToRemove);
}
}
if (!handled && mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1);
}
return handled;
}
事件攔截
intercepted
用來(lái)標(biāo)記ViewGroup
是否攔截事件姥芥,當(dāng)事件為MotionEvent.ACTION_DOWN
或者mFirstTouchTarget!=null
時(shí)兔乞,if
判斷成立,然后判斷disallowIntercept
標(biāo)志位,當(dāng)disallowIntercept
為false
時(shí)庸追,調(diào)用onInterceptTouchEvent()
方法霍骄,并將返回值賦值給intercepted
,否則當(dāng)disallowIntercept
為true
時(shí)锚国,則直接將intercepted
賦值為false
腕巡。
disallowIntercept
標(biāo)記位可以通過(guò)公共方法 requestDisallowInterceptTouchEvent()
設(shè)置,通常由子View
調(diào)用血筑,當(dāng)設(shè)置為true
后绘沉,ViewGroup
將無(wú)法攔截除ACTION_DOWN
之外的點(diǎn)擊事件,原因是當(dāng)事件為ACTION_DOWN
時(shí)豺总,ViewGroup
會(huì)重置disallowIntercept
標(biāo)記位车伞,并且將mFirstTouchTarget
設(shè)置為null
,因此喻喳,當(dāng)事件為ACTION_DOWN
時(shí)另玖,ViewGroup
總是會(huì)調(diào)用自己的onInterceptTouchEvent()
方法。
if (actionMasked == MotionEvent.ACTION_DOWN) {
// 如果是Down事件表伦,則重置所有之前保存的狀態(tài)谦去,因?yàn)檫@是事件序列的開(kāi)始
// mFirstTouchTarget會(huì)被設(shè)為Null
cancelAndClearTouchTargets(ev);
// 重置FLAG_DISALLOW_INTERCEPT
resetTouchState();
}
// 檢測(cè)是否攔截
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
// 標(biāo)記事件不允許被攔截,默認(rèn)為false
// 可以由requestDisallowInterceptTouchEvent方法來(lái)設(shè)置
// 設(shè)置為true蹦哼,ViewGroup將無(wú)法攔截Down以外的點(diǎn)擊事件
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
// 調(diào)用onInterceptTouchEvent(ev)方法鳄哭,詢(xún)問(wèn)自己是否要攔截事件
// ViewGroup的onInterceptTouchEvent(ev)方法默認(rèn)返回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;
}
事件分發(fā)
中間經(jīng)過(guò)標(biāo)記和action
檢查cancel
,將結(jié)果賦值給變量canceled
纲熏。if (!canceled && !intercepted)
語(yǔ)句表明妆丘,事件未被取消且intercepted
為false
(未攔截),則會(huì)進(jìn)入執(zhí)行語(yǔ)句中局劲。
首先判斷childrenCount
不為0
勺拣,然后通過(guò)buildTouchDispatchChildList()
方法拿到子元素的List
集合,接著倒序遍歷所有子元素鱼填,尋找可以接收點(diǎn)擊事件的子元素药有,為什么要倒序遍歷,是因?yàn)?code>buildTouchDispatchChildList()內(nèi)部會(huì)調(diào)用buildOrderedChildList()
方法,該方法會(huì)將子元素根據(jù)Z
軸排序苹丸,在同一Z
平面上的子元素則會(huì)根據(jù)繪制的先后順序排序塑猖,觸摸的時(shí)候我們當(dāng)然會(huì)希望浮在最上層的元素最先響應(yīng)事件。
對(duì)于每一個(gè)子元素來(lái)說(shuō)谈跛,需要canViewReceivePointerEvents()
和isTransformedTouchPointInView()
均返回true
羊苟,才說(shuō)明該子元素可以處理觸摸事件,否則直接continue
進(jìn)行下一次循環(huán)感憾。
canViewReceivePointerEvents()
通過(guò)是否可見(jiàn)及是否在播放動(dòng)畫(huà)來(lái)判斷子元素是否可以接收事件蜡励,isTransformedTouchPointInView()
判斷觸摸事件的坐標(biāo)點(diǎn)是否在子元素內(nèi)令花,這樣我們就獲得了可以處理觸摸事件的子元素。
接下來(lái)通過(guò)getTouchTarget()
方法判斷當(dāng)前子元素是否已經(jīng)賦值給mFirstTouchTarget
凉倚,如果newTouchTarget
不為null
兼都,說(shuō)明子元素已經(jīng)在mFirstTouchTarget
中,執(zhí)行break
跳出循環(huán)稽寒。
如果newTouchTarget
為null
,說(shuō)明子元素并沒(méi)有在mFirstTouchTarget
中保存扮碧,此時(shí)調(diào)用dispatchTransformedTouchEvent()
方法,該方法十分重要杏糙,在該方法內(nèi)部:如果子元素是ViewGroup
并且事件沒(méi)有被攔截慎王,那么遞歸調(diào)用ViewGroup
的dispatchTouchEvent()
;如果子元素是View
,那么調(diào)用View
的dispatchTouchEvent()
宏侍,最終會(huì)調(diào)用View
的onTouchEvent()
赖淤。
dispatchTransformedTouchEvent()
方法是有返回值的,如果返回true
谅河,說(shuō)明子元素消耗了觸摸事件咱旱,則在下面的代碼中將子元素賦值給mFirstTouchEvent
,并跳出循環(huán)绷耍,mFirstTouchTarget
是否被賦值吐限,將直接影響到ViewGroup
對(duì)事件的攔截策略,如果mFirstTouchTarget
為null
褂始,那么ViewGroup
就會(huì)默認(rèn)攔截接下來(lái)同一序列中的所有觸摸事件诸典,這一點(diǎn)在后面分析;如果返回false
,ViewGroup
就會(huì)把事件分發(fā)給下一個(gè)子元素(如果還有下一個(gè)子元素的話)病袄。
// 如果沒(méi)取消也沒(méi)攔截搂赋,進(jìn)入方法體中
if (!canceled && !intercepted) {
View childWithAccessibilityFocus = ev.isTargetAccessibilityFocus()
? findChildWithAccessibilityFocus() : null;
if (actionMasked == MotionEvent.ACTION_DOWN
|| (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
final int actionIndex = ev.getActionIndex(); // always 0 for down
final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)
: TouchTarget.ALL_POINTER_IDS;
// Clean up earlier touch targets for this pointer id in case they
// have become out of sync.
removePointersFromTouchTargets(idBitsToAssign);
final int childrenCount = mChildrenCount;
// 判斷newTouchTarget為Null赘阀,且ChildrenCount不為0
if (newTouchTarget == null && childrenCount != 0) {
final float x = ev.getX(actionIndex);
final float y = ev.getY(actionIndex);
// 尋找可以接受觸摸事件的子View
// 通過(guò)buildTouchDispatchChildList()方法構(gòu)建子View的List集合preorderedList
final ArrayList<View> preorderedList = buildTouchDispatchChildList();
final boolean customOrder = preorderedList == null
&& isChildrenDrawingOrderEnabled();
final View[] children = mChildren;
// 倒序遍歷所有的子View
for (int i = childrenCount - 1; i >= 0; i--) {
final int childIndex = getAndVerifyPreorderedIndex(
childrenCount, i, customOrder);
final View child = getAndVerifyPreorderedView(
preorderedList, children, childIndex);
// If there is a view that has accessibility focus we want it
// to get the event first and if not handled we will perform a
// normal dispatch. We may do a double iteration but this is
// safer given the timeframe.
if (childWithAccessibilityFocus != null) {
if (childWithAccessibilityFocus != child) {
continue;
}
childWithAccessibilityFocus = null;
i = childrenCount - 1;
}
// 同時(shí)滿(mǎn)足兩種情況下子View可以接收事件的分發(fā)
// canViewReceivePointerEvents()方法會(huì)判斷子View是否可見(jiàn)和是否在播放動(dòng)畫(huà)
// isTransformedTouchPointInView()方法會(huì)判斷觸摸事件坐標(biāo)是否在子View內(nèi)
if (!canViewReceivePointerEvents(child)
|| !isTransformedTouchPointInView(x, y, child, null)) {
ev.setTargetAccessibilityFocus(false);
continue;
}
// 查找當(dāng)前子View是否在mFirstTouchTarget中存儲(chǔ)
// 找不到則返回Null
newTouchTarget = getTouchTarget(child);
if (newTouchTarget != null) {
// newTouchTarget不為Nul益缠,說(shuō)明已經(jīng)找到接收的View了,break跳出for循環(huán)
newTouchTarget.pointerIdBits |= idBitsToAssign;
break;
}
resetCancelNextUpFlag(child);
// 沒(méi)有跳出循環(huán)基公,說(shuō)明我們找到的Child并沒(méi)有在mFirstTouchTarget中
// 調(diào)用dispatchTransformedTouchEvent()方法
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();
// 將child賦值給mFirstTouchTarget
newTouchTarget = addTouchTarget(child, idBitsToAssign);
// alreadyDispatchedToNewTouchTarget賦值為true幅慌,跳出循環(huán)
alreadyDispatchedToNewTouchTarget = true;
break;
}
// The accessibility focus didn't handle the event, so clear
// the flag and do a normal dispatch to all children.
ev.setTargetAccessibilityFocus(false);
}
if (preorderedList != null) preorderedList.clear();
}
當(dāng)沒(méi)有任何子元素處理觸摸事件時(shí),調(diào)用dispatchTransformedTouchEvent()
方法轰豆,注意此時(shí)第三個(gè)參數(shù)傳入null
胰伍,在方法內(nèi)部就會(huì)調(diào)用super.dispatchTouchEvent()
,也就是View
類(lèi)的dispatchTouchEvent()
。
if (mFirstTouchTarget == null) {
// 如果mFirstTouchTarget為null
// 調(diào)用dispatchTransformedTouchEvent()方法
// 第三個(gè)參數(shù)為null酸休,會(huì)調(diào)用super.dispatchTouchEvent()方法
// 將當(dāng)前ViewGroup當(dāng)做普通的View處理
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
onInterceptTouchEvent()
方法
if
語(yǔ)句判斷觸摸事件來(lái)源是否為鼠標(biāo)或其他指針式設(shè)備骂租,其他情況下默認(rèn)返回false
。
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (ev.isFromSource(InputDevice.SOURCE_MOUSE)
&& ev.getAction() == MotionEvent.ACTION_DOWN
&& ev.isButtonPressed(MotionEvent.BUTTON_PRIMARY)
&& isOnScrollbarThumb(ev.getX(), ev.getY())) {
return true;
}
return false;
}
dispatchTransformedTouchEvent()
方法
dispatchTransformedTouchEvent()
源碼中發(fā)現(xiàn)多次對(duì)于傳入的child
是否為null
做判斷斑司,并且都做類(lèi)似的操作:
當(dāng)child==null
時(shí)渗饮,調(diào)用super.dispatchTouchEvent()
,也就是View
類(lèi)的dispatchTouchEvent()
方法,因?yàn)?code>ViewGroup的父類(lèi)是View
互站,最終會(huì)調(diào)用View
的onTouchEvent()
方法私蕾。
當(dāng)child!=null
時(shí),遞歸調(diào)用child.dispatchTouchEvent()
胡桃,此時(shí)child
可能是View
踩叭,也可能是ViewGroup
。
從源碼中可以看出dispatchTransformedTouchEvent()
方法的返回值翠胰,最終取決于onTouchEvent()
方法容贝,也就是說(shuō),onTouchEvent()
是否消費(fèi)了事件亡容,決定了dispatchTransformedTouchEvent()
方法的返回值嗤疯,從而決定mFirstTouchTarget
是否為null
。因?yàn)槿绻?code>dispatchTransformedTouchEvent() 方法的返回值為false
闺兢,就無(wú)法執(zhí)行addTouchTarget()
方法茂缚,而mFirstTouchTarget
就是在該方法中被賦值的。
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
View child, int desiredPointerIdBits) {
final boolean handled;
// Canceling motions is a special case. We don't need to perform any transformations
// or filtering. The important part is the action, not the contents.
final int oldAction = event.getAction();
if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
event.setAction(MotionEvent.ACTION_CANCEL);
if (child == null) {
handled = super.dispatchTouchEvent(event);
} else {
handled = child.dispatchTouchEvent(event);
}
event.setAction(oldAction);
return handled;
}
// Calculate the number of pointers to deliver.
final int oldPointerIdBits = event.getPointerIdBits();
final int newPointerIdBits = oldPointerIdBits & desiredPointerIdBits;
// If for some reason we ended up in an inconsistent state where it looks like we
// might produce a motion event with no pointers in it, then drop the event.
if (newPointerIdBits == 0) {
return false;
}
// If the number of pointers is the same and we don't need to perform any fancy
// irreversible transformations, then we can reuse the motion event for this
// dispatch as long as we are careful to revert any changes we make.
// Otherwise we need to make a copy.
final MotionEvent transformedEvent;
if (newPointerIdBits == oldPointerIdBits) {
if (child == null || child.hasIdentityMatrix()) {
if (child == null) {
handled = super.dispatchTouchEvent(event);
} else {
final float offsetX = mScrollX - child.mLeft;
final float offsetY = mScrollY - child.mTop;
event.offsetLocation(offsetX, offsetY);
handled = child.dispatchTouchEvent(event);
event.offsetLocation(-offsetX, -offsetY);
}
return handled;
}
transformedEvent = MotionEvent.obtain(event);
} else {
transformedEvent = event.split(newPointerIdBits);
}
// Perform any necessary transformations and dispatch.
if (child == null) {
handled = super.dispatchTouchEvent(transformedEvent);
} else {
final float offsetX = mScrollX - child.mLeft;
final float offsetY = mScrollY - child.mTop;
transformedEvent.offsetLocation(offsetX, offsetY);
if (! child.hasIdentityMatrix()) {
transformedEvent.transform(child.getInverseMatrix());
}
handled = child.dispatchTouchEvent(transformedEvent);
}
// Done.
transformedEvent.recycle();
return handled;
}
重要結(jié)論
-
ViewGroup
一旦攔截ACTION_DOWN
事件屋谭,那么當(dāng)ACTION_MOVE
脚囊、ACTION_UP
事件到來(lái)時(shí),將不再調(diào)用ViewGroup
的onInterceptTouchEvent()
方法桐磁,并且同一序列中的其他事件都會(huì)默認(rèn)交給它處理悔耘。分析:
ViewGroup
攔截ACTION_DOWN
事件,會(huì)導(dǎo)致intercepted
為true
我擂,從而導(dǎo)致if (!canceled && !intercepted)
判斷不成立衬以,跳過(guò)執(zhí)行語(yǔ)句,mFirstTouchTarget
也為null
校摩。那么當(dāng)ACTION_MOVE
看峻、ACTION_UP
事件到來(lái)時(shí),if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null)
判斷語(yǔ)句不成立衙吩,會(huì)直接將intercepted
賦值為true
互妓,即默認(rèn)攔截后續(xù)的所有事件。 -
某個(gè)
View
一旦開(kāi)始處理事件坤塞,如果它不消費(fèi)ACTION_DOWN
事件冯勉,那么同一事件序列中的其他事件也不會(huì)再交給它來(lái)處理,并且事件將重新交給它的父容器去處理摹芙。分析:某個(gè)
View
不消費(fèi)ACTION_DOWN
事件灼狰,即dispatchTransformedTouchEvent()
方法返回了false
识埋,導(dǎo)致if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign))
判斷語(yǔ)句不成立族操,跳過(guò)執(zhí)行語(yǔ)句,同樣導(dǎo)致mFirstTouchTarget
為null
荷憋,那么和第一條結(jié)論的分析一樣,當(dāng)ACTION_MOVE
承绸、ACTION_UP
事件到來(lái)時(shí)裸影,if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null)
判斷語(yǔ)句不成立,同樣會(huì)直接將intercepted
賦值為true
军熏,所以后續(xù)的事件都無(wú)法傳遞到這個(gè)View
轩猩,而是交給ViewGroup
處理。 ViewGroup
(絕大多數(shù)情況下)默認(rèn)不攔截任何事件荡澎。Android
源碼中ViewGroup
的onInterceptTouchEvent()
方法默認(rèn)返回false
均践。ViewGroup
沒(méi)有重寫(xiě)父類(lèi)View
的onTouchEvent()
方法。
- 到這里就分析完了摩幔,查了很多資料彤委,中間也有可能有理解錯(cuò)誤的地方,如果哪里錯(cuò)了或衡,還請(qǐng)大家指正焦影,謝謝。
- Github