前言
通過(guò)對(duì)ViewGroup與View的dispatchTouchEvent()源碼簡(jiǎn)要分析看View的事件分發(fā)原理.
ViewGroup的dispatchTouchEvent()
局部變量handled代表是否處理,初始化為false.
1.檢測(cè)View是否安全.
onFilterTouchEventForSecurity(),主要是判斷View有沒(méi)有被遮蔽.如果不通過(guò)返回handled.通過(guò)走下一步.
2.重置mGroupFlags,清空touchTarget.
如果是ACTIONDOWN事件,
a.重置mGroupFlags使boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0為false.
b.
mFirstTouchTarget是單鏈表結(jié)構(gòu).在addTOuchTarget()的時(shí)候被賦值,新建一個(gè)Target,next節(jié)點(diǎn)賦值為mFirstTouchTarget,mFirstTouchTarget賦值為新的target.
概括來(lái)說(shuō)一旦有子View處理了事件序列中某一個(gè)事件,那么mFirstTouchTarget就不為空.
遍歷TouchTarget鏈表,釋放清空鏈表,最后mFirstTouchTarget賦值為空.
3.intercepted變量賦值,表示是否攔截.
判斷actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null,為false,intercepted賦值為true.走下一步.
false意味著,當(dāng)不是ACTION_DOWN事件,且mFirstTouchTarget為空(子View沒(méi)有處理事件序列中的任一事件).
所以子View不處理ACTION_DOWN或者攔截了ACTION_DOWN,子View將無(wú)法得到其他的事件
true判斷disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0,true,intercepted賦值為false.false,intercepted = onInterceptTouchEvent(ev).
4.判斷!canceled && !intercepted
canceled的分析//fixme
!canceled && !intercepted為true.遍歷子View,判斷子元素:1.是否可見(jiàn)或者在播放動(dòng)畫(huà)2.事件坐標(biāo)是否在子View的區(qū)域
如果1和2是true調(diào)用dispatchTransformedTouchEvent().dispatchTransformedTouchEvent()在View不為空的情況下就會(huì)調(diào)用子View的dispatchTouchEvent().子View處理了alreadyDispatchedToNewTouchTarget 賦值為true;mFirstTouchTarget賦值.
!canceled && !intercepted為flase.清空狀態(tài),給子View分發(fā)Cancle事件,Target鏈表清空,mFirstTarget賦值為null.
這里反應(yīng)出父View一旦攔截某個(gè)事件,事件序列中之后的事件都將interCepted設(shè)置為true,不會(huì)執(zhí)行onInterCepted()去判斷了.因?yàn)榈冢膊降膍FirstTarget為null.
開(kāi)始這里我是有疑問(wèn)的,外部攔截不是有move部分?jǐn)r截嗎,攔截了那么之后子View就沒(méi)反應(yīng)了?確認(rèn)下代碼,想了想,事件序列這個(gè)概念比較重要.move攔截了一次,那么mFirstTarget為null.就一直攔截了.
這里我又觀察到一個(gè)現(xiàn)象,我在測(cè)試外部攔截處理滑動(dòng)沖突的時(shí)候,move事件的攔截是有條件的,一開(kāi)始不攔截,在ontercepted()打印了幾次不攔截的日志.之后ontercepted()就沒(méi)有再執(zhí)行了.查看源碼只有改變mGroupFlags為false才會(huì)有這種現(xiàn)象.于是我去內(nèi)層的RecyclerView中查找是否調(diào)用requestDisallowInterceptTouchEvent()方法.發(fā)現(xiàn)在onTounchEvent()中就有調(diào)用,發(fā)現(xiàn)有滾動(dòng)消耗就調(diào)用requestDisallowInterceptTouchEvent()方法使得外層mGroupFlags判定結(jié)果是false.
5.handled賦值
判斷mFirstTouchTarget == null
true調(diào)用handled = dispatchTransformedTouchEvent(),執(zhí)行super.dispatchTouchEvent(event)即View的dispatchTouchEvent(event).View的dispatchTouchEvent之后分析.
false根據(jù)子View的處理情況將handled賦值.
View的dispatchTouchEvent()
if (onFilterTouchEventForSecurity(event)) {
if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
result = true;
}
//noinspection SimplifiableIfStatement
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
result = true;
}
if (!result && onTouchEvent(event)) {
result = true;
}
}
在判斷onFilterTouchEventForSecurity后,如果View可用且mOnTouchListener.onTouch(this, event))返回true,就返回true.否則
result = onTouchEvent().