1.ViewGroup中的mFirstTouchTarget是一個(gè)什么東西狠裹,它有什么作用捣卤?
在ViewGroup中有一個(gè)類型為TouchTrarget的mFirstTouchTarget的成員變量,它是用來保存消費(fèi)事件的子View的信息的理疙。代碼如下:
private static final class TouchTarget {
@UnsupportedAppUsage
public View child;
public TouchTarget next;
// ...省略無關(guān)代碼
}
可以看到TouchTarget內(nèi)部保存了一個(gè)View和一個(gè)類型為TouchTarget的next成員變量,也就是說TouchTarget是一個(gè)鏈表結(jié)構(gòu)。為什么是鏈表結(jié)構(gòu)呢悯舟?主要是因?yàn)锳ndroid系統(tǒng)是支持多點(diǎn)觸控的,所以TouchTarget設(shè)計(jì)成了鏈表砸民。
設(shè)計(jì)mFirstTouchTarget的目的是為了避免在所有的事件序列中都去遞歸查找要消費(fèi)事件的View抵怎,只需要在ACTION_DOWN中遞歸查找消費(fèi)的View,并將View封裝后賦值為mFirstTouchTarget岭参,避免了后續(xù)一系列事件的查找反惕。
mFirstTouchTarget會(huì)在ACTION_DOWN的時(shí)候被賦值,查找是否有能夠消費(fèi)事件的子View演侯,如果有則將這個(gè)View包裝成TouchTarget賦值給mFirstTouchTarget姿染,否則mFirstTouchTarget為null。
接下來的一系列ACTION_MOVE事件都會(huì)根據(jù)mFirstTouchTarget是否為null和onInterceptTouchEvent來判斷是否要攔截事件秒际。所以mFirstTouchTarget在事件分發(fā)的流程中占了非常重要的作用悬赏。
2.如果在ViewGroup中攔截了ACTION_DOWN事件會(huì)怎樣?
首先來看ViewGroup的dispatchTouchEvent方法的偽代碼:
// ViewGroup
public boolean dispatchTouchEvent(MotionEvent ev) {
boolean handled = false;
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
// 調(diào)用自身的onInterceptTouchEvent方法來判斷是否攔截事件
intercepted = onInterceptTouchEvent(ev);
} else {
intercepted = true;
}
// 如果在ACTION_DOWN中攔截了事件娄徊,那么intercepted恒為true闽颇,則無法給mFirstTouchTarget賦值
if (!canceled && !intercepted) {
mFirstTouchTarget = findConsumeChild(this);
}
if (mFirstTouchTarget == null) {
// 則調(diào)用自身的dispatchTouchEvent方法分發(fā)事件
handled = super.dispatchTouchEvent(event);
} else{
// 則調(diào)用子View的dispatchTouchEvent方法
handled = mFirstTouchTarget.child.dispatchTouchEvent(event);
}
return handled;
}
從上面的代碼中可以看到,如果在onInterceptTouchEvent方法中攔截ACTION_DOWN事件寄锐,則intercepted為true兵多,而intercepted為true直接導(dǎo)致了mFirstTouchTarget無法被賦值。
接下來橄仆,一系列ACTION_MOVE以及ACTION_UP等事件都無法再調(diào)用onInterceptTouchEvent方法剩膘,也就是intercepted恒為true,且mFirstTouchTarget恒為null沿癞。
再往下援雇,由于mFirstTouchTarget恒為null,就導(dǎo)致了所有的Motion事件(也包括ACTION_DOWN事件)只能交由自身處理椎扬,無法再將事件分發(fā)給子View惫搏。
這一點(diǎn)在事件分發(fā)流程中非常重要具温,通過Down事件確定了要處理該事件的View,接下來所有的Move事件序列就不會(huì)再走遞歸筐赔,而是直接交給這個(gè)View來
3.為什么設(shè)置了onTouchListener后onClickListener不會(huì)被調(diào)用铣猩?
// View
public boolean dispatchTouchEvent(MotionEvent 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;
}
}
return result;
}
在View的dispatchTouchEvent中如果li.mOnTouchListener不為null,則調(diào)用li.mOnTouchListener.onTouch茴丰,而如果li.mOnTouchListener.onTouch返回了true达皿,則下邊的onTouchEvent就不會(huì)被調(diào)用,而onClickListener就是在onTouchEvent方法中才被調(diào)用的贿肩。
// 偽代碼
public boolean onTouchEvent(MotionEvent event) {
public boolean onTouchEvent(MotionEvent event) {
case MotionEvent.ACTION_UP:
li.mOnClickListener.onClick(this);
break;
}
}
4.為什么一個(gè)View設(shè)置了setOnTouchListener會(huì)有提示沒有引用performClick方法的警告峦椰?
當(dāng)你添加了一些點(diǎn)擊操作,例如像setOnClickListener這樣的汰规,它會(huì)調(diào)用performClick才可以完成onClick方法的調(diào)用汤功,但你重寫了onTouch,就有可能使得performClick沒有被調(diào)用溜哮,這樣這些點(diǎn)擊操作就沒辦法完成了滔金,所以就會(huì)有了這個(gè)警告。