原文為我在csdn發(fā)表的博文:
http://blog.csdn.net/sinat_23092639/article/details/74858558
最近工作需要需要做一些比較復(fù)雜的自定義 View,其中事件分發(fā)的處理自然少不了,
結(jié)合之前閱讀過的大量資料,工作是完成了,但是對事件分發(fā)的處理總覺得很不清晰,
知其然不知其所以然的感覺讓人很不舒服。如果不知道事件分發(fā)原理,要是處理的情況
很復(fù)雜的話,那就很難解決了。之前也看過任玉剛的《安卓開發(fā)藝術(shù)探索》對于事件分
發(fā)源碼的分析,但只能說大致了解了事件分發(fā)的流程,而不知其中的道理。網(wǎng)上關(guān)于事件分發(fā)的文章也是非常多,但是總感覺沒說到原理,即是說了也很讓人霧里看花。
索性踐行某位大師的名言——
“read the fucking source”
源碼的閱讀總是讓人痛并快樂著,一是因?yàn)樵创a很長邏輯很復(fù)雜,二是因?yàn)樵创a要考慮
的東西太多,所以干擾的東西實(shí)在太多,經(jīng)常跟進(jìn)去就迷路。
介于本文是對源碼層次的分析,所以如果大家還沒了解過事件分發(fā)的基本流程的話,最
好先看一下這方面的資料。
首先,關(guān)于事件分發(fā),相信接觸過的人都看過類似這樣一張 U 型圖片:

并且也知道 dispatchTouchEvent、InterceptTouchEvent、onTouchEvent 這三個方
法的作用分別分發(fā)事件、攔截事件、消費(fèi)事件。
也看過類似的偽代碼:
public boolean dispatchTouchEvent(MotionEvent ev) {
boolean result = false; // 默認(rèn)狀態(tài)為沒有消費(fèi)過
if (!onInterceptTouchEvent(ev)) { // 如果沒有攔截交給子 View
result = child.dispatchTouchEvent(ev);
}
if (!result) {
// 如果事件沒有被消費(fèi),詢問自身 onTouchEvent
result = onTouchEvent(ev);
}
return result;
}
其實(shí)如果熟悉這些,簡單的事件分發(fā)已經(jīng)可以處理了,但是這些只是單純記憶流程,并
不知道具體的事件在源碼中何去何從。
總的來說,ViewGroup 和 View 屬于樹的結(jié)構(gòu),事件分發(fā)就是從父節(jié)點(diǎn)到子節(jié)點(diǎn)一步
步遍歷的過程,直到找到可以消費(fèi)事件的 View 為止。而在這一過程中,就是一個不斷
遞歸調(diào)用以上偽代碼的過程。子 View 總是在經(jīng)過 dispatchTouchEvent 的執(zhí)行后將
返回值交給父 View,父 View 根據(jù)子 View 是否消費(fèi)了事件再確定自己是否需要消費(fèi)
事件,然后再向自己的父 View 返回一個表示自己或者自己的子 View 是否消費(fèi)了事件
的布爾值。如果當(dāng)前 View(ViewGroup)自己或者子 View 不消費(fèi)就會將事件轉(zhuǎn)給父
View 的 onTouchEvent 方法,所以就會呈現(xiàn)了上面的 U 型圖。分析了源碼之后,更
能體會這其中設(shè)計(jì)的精妙
關(guān)于事件分發(fā)源碼我認(rèn)為最好還是模擬一個事件流,跟著代碼走,努力避開各種其他代
碼的干擾(例如安全檢查等),并且從最簡單的事件入手,即單指觸模、控件不進(jìn)行滑
動、先不考慮 ACTION_CANCEL 事件。
本文基于 Android23 的源碼進(jìn)行分析。請對照 View 和 ViewGroup 的源碼來看本文
首先要明確一個事件序列指的是從手指按下、滑動、抬起的這一個過程中的多個事件。
分別是 DOWN、MOVE、UP 事件。
在這里,假設(shè)一個情景,有一個 Activity,里面的布局是最外層一個 ViewGroup A(充
滿屏幕),A 里面有個 ViewGroup B(充滿 A),B 里面有個 Button C(比如在屏幕
中間)。
情形 1:
A完全(即A上的所有點(diǎn)都要攔截)攔截事件(即InterceptTouchEvent直接返回true),
現(xiàn)在單指點(diǎn)擊了一下 A 范圍任意點(diǎn),然后滑動并抬起。
(Activity、PhoneWindow 、DecorView 的分發(fā)就不進(jìn)行分析了)
現(xiàn)在的事件為 ACTION_DOWN。
首先 DecorView 調(diào)用了 ViewGroup A 的 dispatchTouchEvent 方法(當(dāng)然這里只挑
關(guān)鍵代碼),看到 ViewGroup 的 2103 行:
// Check for interception.
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
//先判斷是否允許該 ViewGroup 攔截事件
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLO
W_INTERCEPT) != 0;
if (!disallowIntercept) {
//判斷是否攔截該事件
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;
}
這一段很簡單,但是很重要。關(guān)鍵在 intercepted = onInterceptTouchEvent(ev);,
大家也很熟悉,此時 A 要攔截事件的,所以 intercepted 為 true。
于是,2134 行的:
if (!canceled && !intercepted)
里面的語句就不會被執(zhí)行(里面的語句主要是遍歷子 View 找到消費(fèi)這一系列事件的子
View)
然后來到 2239 行:
// Dispatch to touch targets.
if (mFirstTouchTarget == null) {
// No touch targets so treat this as an ordinary view.
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
}
mFirstTouchTarget 就是找到的那個要消費(fèi)事件的子 View,此時因?yàn)楦緵]有遍歷過
子 View 去尋找,所以為 null,所以調(diào)用:
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
?
這里 dispatchTransformedTouchEvent 很重要,還要注意第三個參數(shù)傳了 null。
進(jìn)入 dispatchTransformedTouchEvent 方法,先看下注釋:
Transforms a motion event into the coordinate space of a particular child
view,filters out irrelevant pointer ids, and overrides its action if necessary.
If child is null, assumes the MotionEvent will be sent to this ViewGroup instead.
最后一句話說的很明白了,如果 child(即第三個參數(shù))為 null,則該事件會傳給當(dāng)前的 ViewGroup,即 A。
對應(yīng)的代碼在 dispatchTransformedTouchEvent 方法中,處于 ViewGroup 的 2567
行:
// 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);
}
是的,現(xiàn)在調(diào)用了
handled = super.dispatchTouchEvent(transformedEvent);
其實(shí)就是 View 的 dispatchTouchEvent 方法。
在 View 的 dispatchTouchEvent 中,關(guān)鍵看 View 的 9285 行:
if (onFilterTouchEventForSecurity(event)) {
//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;
}
}
?
可以看到如果 View 已經(jīng)設(shè)置了 onTouchListener 并且返回 true,則 onTouchEvent
并不會被執(zhí)行,并且整個 dispatchTouchEvent 方法最后也是返回這個 result。
View 默認(rèn) onTouchListener 為 null,所以 onTouchEvent 會被執(zhí)行。
onTouchEvent 中默認(rèn)主要是對 OnClickListener 和 OnLongClickListener 等事件的
處理??丛创a View 的 10288 行:
if (((viewFlags & CLICKABLE) == CLICKABLE ||
(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) ||
(viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE)
這里的判斷語句如果條件不成立,即假如 View 不是 Clickable(默認(rèn)狀態(tài))的話,
onTouchEvent 則返回 false,當(dāng)然也不執(zhí)行各種點(diǎn)擊事件。
此時如果 A 是默認(rèn)狀態(tài),則 A 的 dispatchTouchEvent 返回 false 給 DecorView,它
的意義是:A 本身和子 View 都不消耗該事件。因?yàn)?DecorView 只有一個子 View A,
如果 A 不消費(fèi)事件,那么在這一系列事件的后續(xù)事件,及 MOVE、UP,就不會分發(fā)給
A 了。
如果 A 是 Clickable 的(比如被 set 了 OnClickListenrer),則 onTouchEvent 返回
true,那么返回 true 給 DecorView,DecorView 就會將后續(xù)的事件都傳給它處理。而
在 ACTION_UP 事件傳遞過來的時候,onTouchEvent 就會觸發(fā) OnClickListenrer 等
的點(diǎn)擊事件。
至于為什么,且聽后面分解。
在這里,可以知道,在 ViewGroup 攔截事件的情況下,會通過
dispatchTransformedTouchEvent去調(diào)用自己的super.dispatchTouchEvent方法最
后調(diào)用 onTouchEvent 方法,也就是把自己當(dāng)做一個 View 處理事件。
dispatchTransformedTouchEvent 很關(guān)鍵,具體下面會說明~~
情形 2:
A 不攔截事件,B 攔截事件,單指點(diǎn)擊屏幕任意點(diǎn),然后滑動并抬起。
同樣,首先 DecorView 調(diào)用了 ViewGroup A 的 dispatchTouchEvent 方法,這時候
intercepted 已經(jīng)是 false 了,所以到了 ViewGroup 的 2134 行的判斷
if (!canceled && !intercepted)
就需要進(jìn)入了(此時的 cancel 為 false,一般情況下都為 false)。
此時來到 ViewGroup 的 2144 行:
if (actionMasked == MotionEvent.ACTION_DOWN
|| (split && actionMasked == MotionEvent.ACTION_POINT
ER_DOWN)
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE)
{
目前只看第一個判斷 actionMasked == MotionEvent.ACTION_DOWN,其實(shí)這個判
斷語句內(nèi)部是用于找出能夠消費(fèi) Down 事件的子 View,這個對于此次系列的事件意義
重大,我們進(jìn)入判斷語句看。
首先看從 ViewGroup 的 2155 行開始的這一段:
final int childrenCount = mChildrenCount;
if (newTouchTarget == null && childrenCount != 0) {
final float x = ev.getX(actionIndex);
final float y = ev.getY(actionIndex);
// Find a child that can receive the event.
// Scan children from front to back.
//從頂?shù)降椎淖?View 集合
final ArrayList<View> preorderedList = buildOrderedChildLis
t();
final boolean customOrder = preorderedList == null
&& isChildrenDrawingOrderEnabled();
final View[] children = mChildren;
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(child
Index);
// If there is a view that has accessibility focus we w
ant it
// to get the event first and if not handled we will per
form 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;
}
if (!canViewReceivePointerEvents(child)
|| !isTransformedTouchPointInView(x, y, child,
null)) {
ev.setTargetAccessibilityFocus(false);
//如果 View 不包含觸摸點(diǎn)則繼續(xù)遍歷
continue;
}
很明顯,ViewGroup 在遍歷子 View,buildOrderedChildList 方法這里是創(chuàng)建了一個
從頂?shù)降椎淖?View 集合去遍歷,所以越頂部的 View 越優(yōu)先可以消費(fèi)事件。最后主要
是使用 isTransformedTouchPointInView 方法判斷觸摸點(diǎn)是否在子 View 上。
B 是 A 的子 View,并且觸摸點(diǎn)在 B 上,所以不會執(zhí)行 continue,即繼續(xù)執(zhí)行后面的代
碼。
來到 ViewGroup 的 2199 行:
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 ori
ginal 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, idBitsTo
Assign);
alreadyDispatchedToNewTouchTarget = true;
break;
}
又見到了 isTransformedTouchPointInView 方法,
進(jìn)入該方法,跳過關(guān)于 ACTION_CANCEL 以及多指觸控的代碼,主要就是一下代碼:
// 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);
}
這里主要是分 child 是否為 null 兩種情況,在這里先不討論 null 的情況,這里的 child
為通過遍歷且確定觸摸點(diǎn)在其中的 View。
當(dāng) child 不為 null 的時候,先做一些滑動偏移量處理,然后調(diào)用 child 的
dispatchTouchEvent 方法。
是的,這里就是真正實(shí)現(xiàn)事件分發(fā)的地方,開始將事件傳遞到子 View 的
dispatchTouchEvent 方法。在已經(jīng)確定觸摸點(diǎn)在 View 上的情況下,調(diào)用
dispatchTransformedTouchEvent 方法的作用是通過自 View 的
dispatchTouchEvent 的返回值來判斷該 View 是否要消費(fèi)這個事件,true 則為消費(fèi),
false 則不消費(fèi)。
現(xiàn)在子View為ViewGroup B,B是攔截事件的,所以interceptTouchEvent返回true,
所有 B 也會像情形 1 中的 A 一樣執(zhí)行 View 的 dispatchTouchEvent 方法,再調(diào)用
onTouchEvent 方法。默認(rèn)情況 onTouchEvent 返回 false,所以不會走入前面 A 的
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
里面的代碼,而是會走到 ViewGroup 2239 行的代碼:
// Dispatch to touch targets.
if (mFirstTouchTarget == null) {
// No touch targets so treat this as an ordinary view.
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
}
是的,這里的 mFirstTouchTarget 是在
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
判斷語句內(nèi)才會被賦值,使得它持有消費(fèi)了事件的 View。
如果 B 的 onTouchEvent 返回 true,即 B 消費(fèi)事件的情況下才會被賦值,它本身為一
個 ViewGroup 內(nèi)部類 TouchTarget 的鏈表,之所以為鏈表主要是為了多指觸摸情況,
這里我們暫且認(rèn)為它代表的是能夠消費(fèi)該事件的 View 就可以。
走到這里是已經(jīng)遍歷完所有 A 的子 View ,如果遍歷完發(fā)現(xiàn)沒有子 View 消費(fèi)事件
(mFirstTouchTarget == null),則調(diào)用:
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
注意到此時 child 參數(shù)傳 null,回看 dispatchTransformedTouchEvent 的代碼就知道
會調(diào)用 super.dispatchTouchEvent,源碼中有關(guān)于此的注釋:
前面已經(jīng)說過了,如果沒有子 View 消費(fèi),當(dāng)前 ViewGroup 就自己調(diào)用
dispatchTouchEvent 嘗試去消費(fèi)。
如果 B 可以消費(fèi)事件呢(B 的 onTouchEvent 返回 true)?那么在 A 遍歷到 B 的時候,
A 的判斷語句中的 dispatchTransformedTouchEvent 就會返回 true(當(dāng)此時 B 已經(jīng)
執(zhí)行完 onTouchEvent 方法),那么就會執(zhí)行前面列出來的,ViewGroup 的 2201 行
開始的代碼。
重點(diǎn)是 ViewGroup 的 2215 行:
newTouchTarget = addTouchTarget(child, idBitsToAssign);
看下 addTouchTarget 方法:
/**
* Adds a touch target for specified child to the beginning of the list.
* Assumes the target child is not already present.
*/
private TouchTarget addTouchTarget(View child, int pointerIdBits) {
TouchTarget target = TouchTarget.obtain(child, pointerIdBits);
target.next = mFirstTouchTarget;
mFirstTouchTarget = target;
return target;
}
其實(shí)就是在以 mFirstTouchTarget 為頭的鏈表插入一個新的頭節(jié)點(diǎn)。不考慮多指觸摸
情況,就是類似賦值 child 給 mFirstTouchTarget 持有的意思。
這里很關(guān)鍵,mFirstTouchTarget 被賦值了,保存的是 A 中需要消費(fèi)事件的子 View,
然后在 dispatchTouchEvent 剩下的代碼中,會在 mFirstTouchTarget 不為 null 的情
況下,返回 true,向 DecorView 報告 A 的子 View 或自己有 View 消費(fèi)事件。
mFirstTouchTarget 有什么意義呢?記得事件分發(fā)中有一條規(guī)則:
一旦一個 View 消費(fèi)了 DOWN 事件,那么該系列的后續(xù)事件都由該 View 處理。
現(xiàn)在 DOWN 事件結(jié)束了,來了 MOVE 事件。
同樣的 A 的 dispatchTouchEvent 方法,又來到 ViewGroup 2144 行的:
if (actionMasked == MotionEvent.ACTION_DOWN
|| (split && actionMasked == MotionEvent.ACTION_POINT
ER_DOWN)
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE)
判斷語句,這次是 ACTION_MOVE 事件,當(dāng)然無法進(jìn)入語句內(nèi)部,于是乎 A 遍歷子
View 找出能夠消費(fèi)事件的 View 都沒有執(zhí)行,直接跳到前面提到的 ViewGroup 的
2240 行的代碼,然后進(jìn)入 2244 行的 else 語句,其中關(guān)鍵是 2251 行代碼:
if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
handled = true;
} else {
final boolean cancelChild = resetCancelNextUpFlag(target.c
hild)
|| intercepted;
if (dispatchTransformedTouchEvent(ev, cancelChild,
target.child, target.pointerIdBits)) {
handled = true;
}
alreadyDispatchedToNewTouchTarget 表示是否是新添加 TouchTarget 的,這個
在上一個事件 DOWN 的時候是被置為 true,但是在這次事件中由于沒有添加新的
TouchTarget,所以為 false。
所以會走到 2256 行:
if (dispatchTransformedTouchEvent(ev, cancelChild,
target.child, target.pointerIdBits)) {
handled = true;
}
這里的 target.child就是 mFirstTouchTarget 中持有的 View,在這里就是ViewGroup
B。所以通過 dispatchTransformedTouchEvent 我們知道這里將當(dāng)前事件
ACTION_MOVE 傳給了 B 的 dispatchTouchEvent 方法。
總的來說就是通過 mFirstTouchTarget 保存 DOWN 事件的消費(fèi) View B,然后在后
續(xù)的事件直接傳給了 B 的 dispatchTouchEvent 處理。
此時如果 B 要消費(fèi)這個 MOVE 事件,則 handle 賦值為 true,則 A 的
dispatchTouchEvent 返回 handle 為 true。如果 B 不要消費(fèi)這個 MOVE 事件,那么
A 的 dispatchTouchEvent 返回 false 給 DecorView。
情形 3:
A 不攔截事件,B 也不攔截事件,單指點(diǎn)擊 Button,然后滑動并抬起。
其實(shí)和情形 2 很相似了。
首先是 Down 事件,DecorView 調(diào)用 A 的 dispatchTouchEvent 方法,A 因?yàn)?br>
onInterceptTouchEvent 返回 false,遍歷點(diǎn)擊到的子 View 找到 B,調(diào)用了 B 的
dispatchTouchEvent 方法,B 因?yàn)?onInterceptTouchEvent 返回 false,遍歷子 View
找到Button C,Button因?yàn)楸旧頌镃lickable,所以dispatchTouchEvent方法返回true
給 B,所以 B 將 C 記錄在 B 的 mFirstTouchTarget 中,然后 B 的 dispatchTouchEvent
返回 true 給 A,告訴它“我這邊可以處理這個事件”,然后 A 將 B 記錄在 A 的
mFirstTouchTarget 中,A 的 dispatchTouchEvent 方法返回 true 給 DecorView。
于是等到 MOVE 事件下來,A 直接找 A 的 mFirstTouchTarget 持有的 View,即 B,B
直接找 B 的 mFirstTouchTarget 持有的 View C,如果 C 要消費(fèi)這個事件那還是一路往上返回 true。
那如果 C 這時候不要消費(fèi)事件呢?那么 C 的 dispatchTouchEvent 返回 false,B 的dispatchTouchEvent 返回 false,且還不能調(diào)用自己的 super.dispatchTouchEvent處理,所以又將 false 返回給 A,A 也和 B 一樣,所以因?yàn)檫f歸最終這個事件交給了Activity 處理。
這就是事件分發(fā)規(guī)則中:
“如果 View 不消耗 DOWN 以外的其他事件,則父 View 不會調(diào)用 onTouchEvent
處理這個事件,同時該 View 仍然可以繼續(xù)接收到后續(xù)的事件,這些 View 不處理的事件都交給 Activtiy 處理”。
那如果 View 不消耗 DOWN 事件呢?其實(shí)前面情形 1 已經(jīng)簡單說過了,這里結(jié)合 A,B,C以及后面兩個情形一起來說會更加直觀。就是 C 如果不接受 DOWN 事件,那么 B 的onTouchEvent 方法會處理事件(不考慮 onTouchListener 情況),如果 B 不可以消費(fèi) DOWN 事件,則調(diào)用 A 的 onTouchEvent 方法處理。所以出現(xiàn)了 U 型圖那樣將事件往上拋的情形。
如果 B 可以消費(fèi) DOWN 事件,那么 C 的mFirstTouchTarget 就記錄為 B,此時 B 的 mFirstTouchTarget 為 null,所以后續(xù)事件來到 C 后,C 直接交給了 B,B 因?yàn)槭录皇?ACTION_DOWN 了,所以不會遍歷子View,直接判斷 mFirstTouchTarget 是否為 null。由于沒有遍歷子 View,所以mFirstTouchTarget 仍然為 null,所以 B 會調(diào)用自己的 super.dispatchTouchEvent處理,以之前分析的類推,后續(xù)事件不會被 C 接收(這部分可以重看下 ViewGroup 的2144 行的判斷語句)。
這就對應(yīng)了另一個事件分發(fā)的規(guī)律:
一旦一個 View 在 onTouchEvent 中不消耗事件,則后續(xù)的事件都不會交給他來處理。
(看源碼發(fā)現(xiàn)如果 DOWN 觸摸點(diǎn)在多個 View 中,應(yīng)該說是這幾個 View 都不消耗事
件為前提?)
最后,當(dāng)一個系列事件結(jié)束之后,新的系列事件來到的時候,會將上一次的 保存的狀
態(tài)清空(比如 mFirstTouchTarget),看 ViewGroup 的 dispatchTouchEvent 在
ViewGroup 中的 2094 行:
// Handle an initial down.
if (actionMasked == MotionEvent.ACTION_DOWN) {
// Throw away all previous state when starting a new touch gestur
e.
// The framework may have dropped the up or cancel event for th
e previous gesture
// due to an app switch, ANR, or some other state change.
cancelAndClearTouchTargets(ev);
resetTouchState();
}
關(guān)于事件分發(fā)機(jī)制就先簡單說到這里,還有很多東西沒有提及,比如多指觸摸、CANCEL
事件等。事件分發(fā)由于涉及遞歸,有時候一層層進(jìn)入又一層層出來很容易讓人迷路。我
也是對源碼研究不深入,難免有疏漏或者錯誤的地方,望各位指正~~