一褒繁,說(shuō)在最前面
我們知道亦鳞,View事件分發(fā)機(jī)制之所以重要是因?yàn)樗粌H可以讓我們理解MotionEvent從Activity到Window然后到ViewRoot最后傳遞到ViewGroup最后到View的整個(gè)過(guò)程中的是否攔截是否消耗以及如何處理事件的詳細(xì)細(xì)節(jié),它在開(kāi)發(fā)中更直接的好處是,他可以給我們處理滑動(dòng)沖突時(shí)提供思路并找到解決的辦法燕差。本篇重點(diǎn)放在View對(duì)事件的消耗處理OnTouch和onTouchEvent上面遭笋。
二,簡(jiǎn)單介紹整個(gè)事件分發(fā)我們會(huì)遇到最重要的三個(gè)函數(shù)
這三個(gè)函數(shù)分別是dispatchTouchEvent徒探、onInterceptTouchEvent瓦呼、和onTouchEvent。
他們的關(guān)系則為:如果能夠傳遞到當(dāng)前View那么他就會(huì)執(zhí)行dispatchTouchEvent方法测暗,然后在dispatchTouchEvent內(nèi)部會(huì)調(diào)用onInterceptionTouchEvent表示是否攔截這個(gè)事件央串,如果攔截就會(huì)嘗試進(jìn)行事件消耗。當(dāng)然消耗事件分為T(mén)ouchListener事件和TouchEvent事件碗啄,這也是在dispatchTouchEvent中完成的质和。下面會(huì)詳細(xì)介紹dispatchTouchEvent函數(shù)中的TouchListener和TouchEvent事件的處理時(shí)機(jī)。關(guān)于事件攔截細(xì)節(jié)以后會(huì)放在處理滑動(dòng)沖突時(shí)候一起介紹稚字。
三饲宿,dispatchTouchEvent
dispatchTouchEvent是處理觸摸事件分發(fā),事件(多數(shù)情況)是從Activity的dispatchTouchEvent開(kāi)始的。執(zhí)行super.dispatchTouchEvent(ev)胆描,事件向下分發(fā)瘫想。返回ture則表示已經(jīng)被消費(fèi),否則返回false昌讲。
在什么條件下會(huì)被消費(fèi)国夜?(截取部分源碼)
if (onFilterTouchEventForSecurity(event)) {
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;
}
}
1,第一個(gè)消耗條件
需要注明的是:
-
li != null
:ListenerInfor里面包含了幾個(gè)Listener短绸,如TouchListener车吹、TouchListener,F(xiàn)ocusChangeListener鸠按,LayoutChangeListeners,ScrollChangeListener等饶碘。 -
li.mOnTouchListener != null
:mOnTouchListener是由View設(shè)置的目尖,比如mButton.setOnTouchListener()。所以如果View設(shè)置了Touch監(jiān)聽(tīng)扎运,那么mOnTouchListener不空瑟曲;反之為null。 -
(mViewFlags & ENABLED_MASK) == ENABLED
:表示當(dāng)前view是否可用豪治。 -
li.mOnTouchListener.onTouch(this, event)
:表示onTouch是否被消耗洞拨。
只有上面四個(gè)條件同時(shí)滿足才會(huì)返回true表示消耗了此事件。
2负拟,第二個(gè)消耗條件
如果這幾個(gè)條件有一個(gè)沒(méi)有滿足烦衣,那么就看第二個(gè)if語(yǔ)句。
那么此時(shí)關(guān)鍵在于onTouchEvent這個(gè)方法,如果onTouEvent返回true花吟,那么就表示消耗了這個(gè)事件秸歧。可以看出onTouchListener的優(yōu)先級(jí)比onTouchEvent優(yōu)先級(jí)高衅澈,onTouchEvent源碼:
public boolean onTouchEvent(MotionEvent event) {
final float x = event.getX();
final float y = event.getY();
final int viewFlags = mViewFlags;
final int action = event.getAction();
if ((viewFlags & ENABLED_MASK) == DISABLED) {
if (action == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {
setPressed(false);
}
return (((viewFlags & CLICKABLE) == CLICKABLE
|| (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
|| (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE);
}
if (mTouchDelegate != null) {
if (mTouchDelegate.onTouchEvent(event)) {
return true;
}
}
if (((viewFlags & CLICKABLE) == CLICKABLE ||
(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) ||
(viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE) {
switch (action) {
case MotionEvent.ACTION_UP:
boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;
if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
boolean focusTaken = false;
if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
focusTaken = requestFocus();
}
if (prepressed) {
setPressed(true, x, y);
}
if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {
removeLongPressCallback();
if (!focusTaken) {
if (mPerformClick == null) {
mPerformClick = new PerformClick();
}
if (!post(mPerformClick)) {
performClick();
}
}
if (mUnsetPressedState == null) {
mUnsetPressedState = new UnsetPressedState();
}
if (prepressed) {
postDelayed(mUnsetPressedState,
ViewConfiguration.getPressedStateDuration());
} else if (!post(mUnsetPressedState)) {
mUnsetPressedState.run();
}
removeTapCallback();
}
mIgnoreNextUpEvent = false;
break;
case MotionEvent.ACTION_DOWN:
mHasPerformedLongPress = false;
if (performButtonActionOnTouchDown(event)) {
break;
}
boolean isInScrollingContainer = isInScrollingContainer();
if (isInScrollingContainer) {
mPrivateFlags |= PFLAG_PREPRESSED;
if (mPendingCheckForTap == null) {
mPendingCheckForTap = new CheckForTap();
}
mPendingCheckForTap.x = event.getX();
mPendingCheckForTap.y = event.getY();
postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
} else {
setPressed(true, x, y);
checkForLongClick(0);
}
break;
case MotionEvent.ACTION_CANCEL:
setPressed(false);
removeTapCallback();
removeLongPressCallback();
mInContextButtonPress = false;
mHasPerformedLongPress = false;
mIgnoreNextUpEvent = false;
break;
case MotionEvent.ACTION_MOVE:
drawableHotspotChanged(x, y);
if (!pointInView(x, y, mTouchSlop)) {
removeTapCallback();
if ((mPrivateFlags & PFLAG_PRESSED) != 0) {
removeLongPressCallback();
setPressed(false);
}
}
break;
}
return true;
}
return false;
}
第一種情況
先看第一個(gè)if語(yǔ)句键菱,如果這個(gè)view不可用,但是只要他滿足CLICKABLE或LONG_CLICKABLE或CONTEXT_CLICKABLE的就返回true今布。但是沒(méi)有處理點(diǎn)擊事件经备。
第二種情況
如果這個(gè)view可用,看第三個(gè)if語(yǔ)句部默,如果CLICKABLE或LONG_CLICKABLE或CONTEXT_CLICKABLE滿足任何一個(gè)侵蒙,那么就返回true,也就是dispatchTouchEvent返回true甩牺,消耗了這個(gè)事件蘑志。如果都不滿足就返回false,也就是沒(méi)有消耗這個(gè)事件贬派。
需要注意的是在第二種情況中當(dāng)滿足條件的時(shí)候急但,if語(yǔ)句里面判斷MotionEvent.ACTION_UP:
的時(shí)候,如果滿足里面的情況會(huì)調(diào)用performClick()搞乏,也就是調(diào)用了onClick事件波桩。
3,需要注意的一些細(xì)節(jié)
- 不同的view的clickable屬性有差別请敦。比如:Button默認(rèn)clickable屬性為true镐躲。
- view的longClickable默認(rèn)屬性為false。
- 我們可以通過(guò)兩種方式改變這個(gè)屬性侍筛。
- setClickable()和setLongClickListener()
- 設(shè)置監(jiān)聽(tīng)萤皂。如:setOnClickListener()會(huì)將View的CLICKABLE設(shè)置為true;setOnLongClickListener()會(huì)將View的LONG_CLICKABLE設(shè)置為true匣椰。
4裆熙,對(duì)于OnTouch和onTouchEvent的補(bǔ)充
上面只是對(duì)處理的時(shí)機(jī)進(jìn)行介紹,我們還需要知道的是這兩個(gè)事件的其他區(qū)別
- ontouch他的處理粒度更小禽笑,他可以處理down入录、move和up組成的各種事件。
- 但是onTouchEvent只是onClick監(jiān)聽(tīng)事件佳镜。
四僚稿,onInterceptTouchEvent
onInterceptTouchEvent是ViewGroup提供的方法,默認(rèn)返回false蟀伸,返回true表示攔截蚀同。
五缅刽,onTouchEvent
onTouchEvent是View中提供的方法,ViewGroup也有這個(gè)方法唤崭,view中不提供onInterceptTouchEvent拷恨。view中默認(rèn)返回true,表示消費(fèi)了這個(gè)事件谢肾。
六腕侄,View,ViewGroup和Activity里面的回調(diào)函數(shù)
1芦疏,view里面只有兩個(gè)回調(diào)函數(shù)
public boolean dispatchTouchEvent(MotionEvent ev)冕杠;
public boolean onTouchEvent(MotionEvent ev);
2,ViewGroup里面有三個(gè)回調(diào)函數(shù)
public boolean dispatchTouchEvent(MotionEvent ev);
public boolean onInterceptTouchEvent(MotionEvent ev);
public boolean onTouchEvent(MotionEvent ev);
3,Activity中的兩個(gè)回調(diào)函數(shù)
public boolean dispatchTouchEvent(MotionEvent ev)酸茴;
public boolean onTouchEvent(MotionEvent ev);