在Android-27中查看源碼:
在看完View源碼的觸摸事件(參考文章:View源碼-Touch事件)后,我們接著來看看容器類View的觸摸事件土榴。因?yàn)槿萜黝惖腣iew都繼承自ViewGroup,所以我們?cè)赩iewGroup的源碼中來看看是如何處理觸摸事件的艰毒。
同樣的先來看ViewGroup源碼中的dispatchTouchEvent方法:
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) {
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_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;
}
首先愤诱,會(huì)判斷是否不允許攔截,可以通過requestDisallowInterceptTouchEvent進(jìn)行設(shè)置酒请,默認(rèn)允許攔截。如果允許攔截鸣个,會(huì)調(diào)用onInterceptTouchEvent羞反,看看該View是否攔截了觸摸事件布朦,默認(rèn)不攔截即可以將觸摸事件向子View傳遞。
接下來昼窗,如果觸摸事件沒有取消并且沒有攔截是趴,在手指按下的時(shí)候:
final ArrayList<View> preorderedList = buildTouchDispatchChildList();
...
for (int i = childrenCount - 1; i >= 0; i--){
...
newTouchTarget = getTouchTarget(child);
if (newTouchTarget != null) {
// Child is already receiving touch within its bounds.
// Give it the new pointer in addition to the ones it is handling.
newTouchTarget.pointerIdBits |= idBitsToAssign;
break;
}
resetCancelNextUpFlag(child);
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;
}
...
}
- 按照繪制子View的順序,找到該ViewGoup的所有子view澄惊,并保存到ArrayList中唆途。
- 根據(jù)視圖順序依次遍歷子View。判斷當(dāng)前子view是否是TouchTarget掸驱,若是則跳出循環(huán)肛搬。否則調(diào)用dispatchTransformedTouchEvent方法,如果當(dāng)前子View的dispatchTouchEvent返回為true,找到該子View在ViewGroup中的index,并將該子View作為新的TouchTarget毕贼。
- 清楚保存了所有子View的ArrayList温赔。
然后在TouchTarget形成的鏈?zhǔn)浇Y(jié)構(gòu)中,處理觸摸事件:
// 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);
} 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;
}
}
- 如果子View沒有處理觸摸事件鬼癣,則由當(dāng)前的ViewGroup處理让腹,然后返回處理結(jié)果。因?yàn)閂iewGroup是View的子類扣溺,所以還是由View的dispatchTouchEvent處理。
- 如果子View中處理了觸摸事件瓜晤,根據(jù)TouchTarget生成的鏈?zhǔn)浇Y(jié)構(gòu)锥余,不斷循環(huán),分別調(diào)用里面View的dispatchTouchEvent方法痢掠。
從上面的分析可以看出整個(gè)ViewGroup的Touch事件的整個(gè)傳遞過程如下:
- 是否調(diào)用了requestDisallowInterceptTouchEvent方法設(shè)置不允許攔截驱犹,默認(rèn)允許攔截。若允許攔截足画,則再調(diào)用onInterceptTouchEvent,看是否攔截雄驹。
- 如果觸摸事件被攔截,則調(diào)用ViewGroup的dispatchTransformedTouchEvent方法淹辞,其實(shí)是調(diào)用View的dispatchTouchEvent方法医舆。否則繼續(xù)向下。
- 當(dāng)觸摸事件為MotionEvent.ACTION_DOWN時(shí)象缀,首先獲取根據(jù)繪制順序保存了所有子View的ArrayLsit蔬将,然后再根據(jù)視圖順序去遍歷子View。
i. 如果從TouchTarget形成的鏈表中發(fā)現(xiàn)該View,則跳出循環(huán)央星,即找到了TouchTarget霞怀。否則繼續(xù)向下。
ii. 在子View中尋找莉给,當(dāng)子View的dispatchTouchEvent方法返回為true時(shí),則找到了新的TouchTarget毙石,并將其添加到TouchTarget形成的鏈表中廉沮。 - 遍歷TouchTarget形成的鏈表,然后對(duì)鏈表里面的子View分別調(diào)用dispatchTouchEvent徐矩,最后將處理結(jié)果返回滞时。