- dispatchTouchEvent()、onInterceptTouchEvent()和onTouchEvent()方法源碼解析
- 各種觸摸事件分發、消費情況詳解
dispatchTouchEvent()、onInterceptTouchEvent()和onTouchEvent()方法源碼解析
Android觸摸事件分發過程中最重要的就是dispatchTouchEvent()
、onInterceptTouchEvent()
和onTouchEvent()
方法。其中onInterceptTouchEvent()
方法只存在於ViewGroup中,View中沒有,其用於事件的攔截。dispatchTouchEvent()
方法用於事件的分發,onTouchEvent()
方法用於事件的消費。
下面給出這三個主要方法的部分主要源碼實現,並做出分析。
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;
}
這段代碼用來判斷是否攔截該事件,從代碼中可知,當觸發down事件或已存在之前消費過事件的對象時,需要通過disallowIntercept
和onInterceptTouchEvent(ev)
兩個條件來判斷是否要攔截該事件。其中disallowIntercept
默認得到的值爲false,即允許攔截,其值可通過requestDisallowInterceptTouchEvent
方法設置,當disallowIntercept爲false時,接下來調用onInterceptTouchEvent(ev)
方法進行判斷。所以可通過重寫onInterceptTouchEvent(ev)
方法對各種事件進行具體的事件攔截。
dispatchTouchEvent
中還會間接調用到如下代碼段:
// 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);
}
這段代碼展示了,ViewGroup的dispatchTouchEvent
會調用其子視圖的dispatchTouchEvent
方法,將事件向下分發。
事件以上面的形式,一層層向內傳遞,若未經攔截,到達View的dispatchTouchEvent
方法時,其中有下面一段代碼:
//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;
}
這段代碼中,有一個很長的判定條件,其結果決定了是否執行onTouchEvent
方法。首先我們看mOnTouchListener在哪進行設置,找到如下代碼:
public void setOnTouchListener(OnTouchListener l) {
getListenerInfo().mOnTouchListener = l;
}
ListenerInfo getListenerInfo() {
if (mListenerInfo != null) {
return mListenerInfo;
}
mListenerInfo = new ListenerInfo();
return mListenerInfo;
}
所以當對視圖調用setOnTouchListener
方法設置監聽後,li != null && li.mOnTouchListener != null
兩個條件就得到了滿足,而(mViewFlags & ENABLED_MASK) == ENABLED
的判斷是該視圖是否處於enable狀態,其默認爲true,所以當OnTouchListener中的onTouch
方法返回true時,就攔截了事件向onTouchEvent
方法傳遞。下面看一下View的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);
}
// A disabled view that is clickable still consumes the touch
// events, it just doesn't respond to them.
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) {
// take focus if we don't have it already and we should in
// touch mode.
boolean focusTaken = false;
if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
focusTaken = requestFocus();
}
if (prepressed) {
// The button is being released before we actually
// showed it as pressed. Make it show the pressed
// state now (before scheduling the click) to ensure
// the user sees it.
setPressed(true, x, y);
}
if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {
// This is a tap, so remove the longpress check
removeLongPressCallback();
// Only perform take click actions if we were in the pressed state
if (!focusTaken) {
// Use a Runnable and post this rather than calling
// performClick directly. This lets other visual state
// of the view update before click actions start.
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)) {
// If the post failed, unpress right now
mUnsetPressedState.run();
}
removeTapCallback();
}
mIgnoreNextUpEvent = false;
break;
case MotionEvent.ACTION_DOWN:
mHasPerformedLongPress = false;
if (performButtonActionOnTouchDown(event)) {
break;
}
// Walk up the hierarchy to determine if we're inside a scrolling container.
boolean isInScrollingContainer = isInScrollingContainer();
// For views inside a scrolling container, delay the pressed feedback for
// a short period in case this is a scroll.
if (isInScrollingContainer) {
mPrivateFlags |= PFLAG_PREPRESSED;
if (mPendingCheckForTap == null) {
mPendingCheckForTap = new CheckForTap();
}
mPendingCheckForTap.x = event.getX();
mPendingCheckForTap.y = event.getY();
postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
} else {
// Not inside a scrolling container, so show the feedback right away
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);
// Be lenient about moving outside of buttons
if (!pointInView(x, y, mTouchSlop)) {
// Outside button
removeTapCallback();
if ((mPrivateFlags & PFLAG_PRESSED) != 0) {
// Remove any future long press/tap checks
removeLongPressCallback();
setPressed(false);
}
}
break;
}
return true;
}
return false;
}
方法很長,其中定義了對down,move,up等事件的處理。我們知道如果在視圖上設置了OnTouchListener並在其onTouch方法中返回true,該視圖的onClick
方法將不再被觸發,我們還知道,onClick
方法在up事件時被觸發。我們查看上述代碼對up事件的處理時,發現有一個performClick
方法,該方法的代碼如下:
public boolean performClick() {
final boolean result;
final ListenerInfo li = mListenerInfo;
if (li != null && li.mOnClickListener != null) {
playSoundEffect(SoundEffectConstants.CLICK);
li.mOnClickListener.onClick(this);
result = true;
} else {
result = false;
}
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
return result;
}
是不是看到了li.mOnClickListener.onClick(this)
這行代碼,這就是onClick
被觸發回調的方法,通過上面的分析知道,如果設置的OnTouchListener的onTouch
方法中返回true,則將不會執行到這段回調代碼,所以導致onClick
方法失效。
所以總體上,當一個觸摸事件開始時,最外層ViewGroup捕獲該事件,並開始調用其dispatchTouchEvent
方法進行事件分發,該方法會根據本次觸摸的之前事件的攔截和消費情況,來決定怎樣調用其內層的ViewGroup的dispatchTouchEvent
,onInterceptTouchEvent
和onTouchEvent
方法或View的dispatchTouchEvent
和onTouchEvent
方法,來將事件更深層的分發和消費,這些方法的處理結果,又逐級回傳,從而進一步影響後面事件的分發和消費方式。
接下來,以上面的源碼分析爲基礎,具體分析觸摸事件分發處理情況。
各種觸摸事件分發、消費情況詳解
ACTION_DOWN事件未被消費,分發失敗
在一個觸摸事件中,down事件起着至關重要的作用,down事件就是“敲門”事件,如果門沒有敲開,後面的事件也就沒有意義了,所以如果down事件沒有被分發出去,即沒有被消費,那麼後面的事件也就不會被分發了。
說明:
- 都不攔截ACTION_DOWN事件,會依次向下傳遞
- 都不消費ACTION_DOWN事件,
onTouchEvent
會依次向上傳遞 - 都不消費ACTION_DOWN事件,
dispatchTouchEvent
會返回false,表示事件沒有被派發出去 - ACTION_DOWN事件沒有被消費,後續的ACTION_MOVE、ACTION_UP等事件都不會再被傳遞
示例程序:
Override
public boolean dispatchTouchEvent(MotionEvent ev) {
int action = ev.getAction();
String motionEvent = null;
switch (action) {
case MotionEvent.ACTION_DOWN:
motionEvent = "down";
return false;
case MotionEvent.ACTION_MOVE:
motionEvent = "move";
break;
case MotionEvent.ACTION_UP:
motionEvent = "up";
break;
case MotionEvent.ACTION_CANCEL:
motionEvent = "cancel";
break;
}
Log.i(SXD, TAG + "--dispatchTouchEvent++in++motionEvent:" + motionEvent);
boolean ret = super.dispatchTouchEvent(ev);
Log.i(SXD, TAG + "--dispatchTouchEvent++out++ret:" + ret + ",motionEvent:" + motionEvent);
return ret;
}
該程序沒有log輸出,則證明down事件沒有分發成功後,在沒有後續事件進行分發。
ACTION_DOWN事件被消費,後續事件被攔截
如果視圖消費了down事件,且之後有事件被攔截,則每種被攔截事件類型(move,up等)都會觸發收到一次cancel事件。
示例程序:
onInterceptTouchEvent在Move事件返回true
Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
int action = ev.getAction();
String motionEvent = null;
switch (action) {
case MotionEvent.ACTION_DOWN:
motionEvent = "down";
break;
case MotionEvent.ACTION_MOVE:
motionEvent = "move";
return true;
case MotionEvent.ACTION_UP:
motionEvent = "up";
break;
case MotionEvent.ACTION_CANCEL:
motionEvent = "cancel";
break;
}
Log.i(SXD, TAG + "--onInterceptTouchEvent++in++motionEvent:" + motionEvent);
boolean ret = super.onInterceptTouchEvent(ev);
Log.i(SXD, TAG + "--onInterceptTouchEvent++out++ret:" + ret + ",motionEvent:" + motionEvent);
return ret;
}
I/sxd: TouchTestRelativeLayout--dispatchTouchEvent++in++motionEvent:down
I/sxd: TouchTestRelativeLayout--onInterceptTouchEvent++in++motionEvent:down
I/sxd: TouchTestRelativeLayout--onInterceptTouchEvent++out++ret:false,motionEvent:down
I/sxd: TouchButton--dispatchTouchEvent++in++motionEvent:down
I/sxd: TouchButton--onTouch++ret:false,motionEvent:down
I/sxd: TouchButton--onTouchEvent++in++motionEvent:down
I/sxd: TouchButton--onTouchEvent++out++ret:true,motionEvent:down
I/sxd: TouchButton--dispatchTouchEvent++out++ret:true,motionEvent:down
I/sxd: TouchTestRelativeLayout--dispatchTouchEvent++out++ret:true,motionEvent:down
I/sxd: TouchTestRelativeLayout--dispatchTouchEvent++in++motionEvent:move
I/sxd: TouchButton--dispatchTouchEvent++in++motionEvent:cancel
I/sxd: TouchButton--onTouch++ret:false,motionEvent:cancel
I/sxd: TouchButton--onTouchEvent++in++motionEvent:cancel
I/sxd: TouchButton--onTouchEvent++out++ret:true,motionEvent:cancel
I/sxd: TouchButton--dispatchTouchEvent++out++ret:true,motionEvent:cancel
I/sxd: TouchTestRelativeLayout--dispatchTouchEvent++out++ret:true,motionEvent:move
I/sxd: TouchTestRelativeLayout--dispatchTouchEvent++in++motionEvent:move
I/sxd: TouchTestRelativeLayout--onTouch++ret:false,motionEvent:move
I/sxd: TouchTestRelativeLayout--onTouchEvent++in++motionEvent:move
I/sxd: TouchTestRelativeLayout--onTouchEvent++out++ret:true,motionEvent:move
I/sxd: TouchTestRelativeLayout--dispatchTouchEvent++out++ret:true,motionEvent:move
I/sxd: TouchTestRelativeLayout--dispatchTouchEvent++in++motionEvent:move
I/sxd: TouchTestRelativeLayout--onTouch++ret:false,motionEvent:move
I/sxd: TouchTestRelativeLayout--onTouchEvent++in++motionEvent:move
I/sxd: TouchTestRelativeLayout--onTouchEvent++out++ret:true,motionEvent:move
I/sxd: TouchTestRelativeLayout--dispatchTouchEvent++out++ret:true,motionEvent:move
I/sxd: TouchTestRelativeLayout--dispatchTouchEvent++in++motionEvent:up
I/sxd: TouchTestRelativeLayout--onTouch++ret:false,motionEvent:up
I/sxd: TouchTestRelativeLayout--onTouchEvent++in++motionEvent:up
I/sxd: TouchTestRelativeLayout--onTouchEvent++out++ret:true,motionEvent:up
I/sxd: TouchTestRelativeLayout--dispatchTouchEvent++out++ret:true,motionEvent:up
結果表明,當onInterceptTouchEvent
在move事件時返回true進行事件攔截時,之前的down事件可以向下分發,但第一次的move事件將觸發之前處理down事件的子View觸發cancel事件,且本次move事件不會被攔截層消費,但之後的move,up等事件可被攔截層消費。
onInterceptTouchEvent在Up事件返回true
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
int action = ev.getAction();
String motionEvent = null;
switch (action) {
case MotionEvent.ACTION_DOWN:
motionEvent = "down";
break;
case MotionEvent.ACTION_MOVE:
motionEvent = "move";
break;
case MotionEvent.ACTION_UP:
motionEvent = "up";
return true;
case MotionEvent.ACTION_CANCEL:
motionEvent = "cancel";
break;
}
Log.i(SXD, TAG + "--onInterceptTouchEvent++in++motionEvent:" + motionEvent);
boolean ret = super.onInterceptTouchEvent(ev);
Log.i(SXD, TAG + "--onInterceptTouchEvent++out++ret:" + ret + ",motionEvent:" + motionEvent);
return ret;
}
I/sxd: TouchTestRelativeLayout--dispatchTouchEvent++in++motionEvent:down
I/sxd: TouchTestRelativeLayout--onInterceptTouchEvent++in++motionEvent:down
I/sxd: TouchTestRelativeLayout--onInterceptTouchEvent++out++ret:false,motionEvent:down
I/sxd: TouchButton--dispatchTouchEvent++in++motionEvent:down
I/sxd: TouchButton--onTouch++ret:false,motionEvent:down
I/sxd: TouchButton--onTouchEvent++in++motionEvent:down
I/sxd: TouchButton--onTouchEvent++out++ret:true,motionEvent:down
I/sxd: TouchButton--dispatchTouchEvent++out++ret:true,motionEvent:down
I/sxd: TouchTestRelativeLayout--dispatchTouchEvent++out++ret:true,motionEvent:down
I/sxd: TouchTestRelativeLayout--dispatchTouchEvent++in++motionEvent:move
I/sxd: TouchTestRelativeLayout--onInterceptTouchEvent++in++motionEvent:move
I/sxd: TouchTestRelativeLayout--onInterceptTouchEvent++out++ret:false,motionEvent:move
I/sxd: TouchButton--dispatchTouchEvent++in++motionEvent:move
I/sxd: TouchButton--onTouch++ret:false,motionEvent:move
I/sxd: TouchButton--onTouchEvent++in++motionEvent:move
I/sxd: TouchButton--onTouchEvent++out++ret:true,motionEvent:move
I/sxd: TouchButton--dispatchTouchEvent++out++ret:true,motionEvent:move
I/sxd: TouchTestRelativeLayout--dispatchTouchEvent++out++ret:true,motionEvent:move
I/sxd: TouchTestRelativeLayout--dispatchTouchEvent++in++motionEvent:move
I/sxd: TouchTestRelativeLayout--onInterceptTouchEvent++in++motionEvent:move
I/sxd: TouchTestRelativeLayout--onInterceptTouchEvent++out++ret:false,motionEvent:move
I/sxd: TouchButton--dispatchTouchEvent++in++motionEvent:move
I/sxd: TouchButton--onTouch++ret:false,motionEvent:move
I/sxd: TouchButton--onTouchEvent++in++motionEvent:move
I/sxd: TouchButton--onTouchEvent++out++ret:true,motionEvent:move
I/sxd: TouchButton--dispatchTouchEvent++out++ret:true,motionEvent:move
I/sxd: TouchTestRelativeLayout--dispatchTouchEvent++out++ret:true,motionEvent:move
I/sxd: TouchTestRelativeLayout--dispatchTouchEvent++in++motionEvent:move
I/sxd: TouchTestRelativeLayout--onInterceptTouchEvent++in++motionEvent:move
I/sxd: TouchTestRelativeLayout--onInterceptTouchEvent++out++ret:false,motionEvent:move
I/sxd: TouchButton--dispatchTouchEvent++in++motionEvent:move
I/sxd: TouchButton--onTouch++ret:false,motionEvent:move
I/sxd: TouchButton--onTouchEvent++in++motionEvent:move
I/sxd: TouchButton--onTouchEvent++out++ret:true,motionEvent:move
I/sxd: TouchButton--dispatchTouchEvent++out++ret:true,motionEvent:move
I/sxd: TouchTestRelativeLayout--dispatchTouchEvent++out++ret:true,motionEvent:move
I/sxd: TouchTestRelativeLayout--dispatchTouchEvent++in++motionEvent:up
I/sxd: TouchButton--dispatchTouchEvent++in++motionEvent:cancel
I/sxd: TouchButton--onTouch++ret:false,motionEvent:cancel
I/sxd: TouchButton--onTouchEvent++in++motionEvent:cancel
I/sxd: TouchButton--onTouchEvent++out++ret:true,motionEvent:cancel
I/sxd: TouchButton--dispatchTouchEvent++out++ret:true,motionEvent:cancel
I/sxd: TouchTestRelativeLayout--dispatchTouchEvent++out++ret:true,motionEvent:up
結果表明,當onInterceptTouchEvent
在up事件時返回true進行事件攔截時,之前的觸摸事件可以向下分發,但本次up事件將觸發之前處理事件的子View觸發cancel事件,且本次cancel事件不會被攔截層消費。
ACTION_DOWN事件被攔截
如果視圖沒有消費down事件,則之後的事件不會再向其分發。
示例程序:
初始down事件被上層攔截
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
int action = ev.getAction();
String motionEvent = null;
switch (action) {
case MotionEvent.ACTION_DOWN:
motionEvent = "down";
return true;
case MotionEvent.ACTION_MOVE:
motionEvent = "move";
break;
case MotionEvent.ACTION_UP:
motionEvent = "up";
break;
case MotionEvent.ACTION_CANCEL:
motionEvent = "cancel";
break;
}
Log.i(SXD, TAG + "--onInterceptTouchEvent++in++motionEvent:" + motionEvent);
boolean ret = super.onInterceptTouchEvent(ev);
Log.i(SXD, TAG + "--onInterceptTouchEvent++out++ret:" + ret + ",motionEvent:" + motionEvent);
return ret;
}
I/sxd: TouchTestRelativeLayout--dispatchTouchEvent++in++motionEvent:down
I/sxd: TouchTestRelativeLayout--onTouch++ret:false,motionEvent:down
I/sxd: TouchTestRelativeLayout--onTouchEvent++in++motionEvent:down
I/sxd: TouchTestRelativeLayout--onTouchEvent++out++ret:true,motionEvent:down
I/sxd: TouchTestRelativeLayout--dispatchTouchEvent++out++ret:true,motionEvent:down
I/sxd: TouchTestRelativeLayout--dispatchTouchEvent++in++motionEvent:move
I/sxd: TouchTestRelativeLayout--onTouch++ret:false,motionEvent:move
I/sxd: TouchTestRelativeLayout--onTouchEvent++in++motionEvent:move
I/sxd: TouchTestRelativeLayout--onTouchEvent++out++ret:true,motionEvent:move
I/sxd: TouchTestRelativeLayout--dispatchTouchEvent++out++ret:true,motionEvent:move
I/sxd: TouchTestRelativeLayout--dispatchTouchEvent++in++motionEvent:move
I/sxd: TouchTestRelativeLayout--onTouch++ret:false,motionEvent:move
I/sxd: TouchTestRelativeLayout--onTouchEvent++in++motionEvent:move
I/sxd: TouchTestRelativeLayout--onTouchEvent++out++ret:true,motionEvent:move
I/sxd: TouchTestRelativeLayout--dispatchTouchEvent++out++ret:true,motionEvent:move
I/sxd: TouchTestRelativeLayout--dispatchTouchEvent++in++motionEvent:up
I/sxd: TouchTestRelativeLayout--onTouch++ret:false,motionEvent:up
I/sxd: TouchTestRelativeLayout--onTouchEvent++in++motionEvent:up
I/sxd: TouchTestRelativeLayout--onTouchEvent++out++ret:true,motionEvent:up
I/sxd: TouchTestRelativeLayout--dispatchTouchEvent++out++ret:true,motionEvent:up
I/sxd: TouchTestRelativeLayout--onClick
View未消費down事件
@Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
String motionEvent = null;
switch (action) {
case MotionEvent.ACTION_DOWN:
motionEvent = "down";
return false;
case MotionEvent.ACTION_MOVE:
motionEvent = "move";
break;
case MotionEvent.ACTION_UP:
motionEvent = "up";
break;
case MotionEvent.ACTION_CANCEL:
motionEvent = "cancel";
break;
}
Log.i(SXD, TAG + "--onTouchEvent++in++motionEvent:" + motionEvent);
boolean ret = super.onTouchEvent(event);
Log.i(SXD, TAG + "--onTouchEvent++out++ret:" + ret + ",motionEvent:" + motionEvent);
return ret;
}
I/sxd: TouchTestRelativeLayout--dispatchTouchEvent++in++motionEvent:down
I/sxd: TouchTestRelativeLayout--onInterceptTouchEvent++in++motionEvent:down
I/sxd: TouchTestRelativeLayout--onInterceptTouchEvent++out++ret:false,motionEvent:down
I/sxd: TouchButton--dispatchTouchEvent++in++motionEvent:down
I/sxd: TouchButton--onTouch++ret:false,motionEvent:down
I/sxd: TouchButton--dispatchTouchEvent++out++ret:false,motionEvent:down
I/sxd: TouchTestRelativeLayout--onTouch++ret:false,motionEvent:down
I/sxd: TouchTestRelativeLayout--onTouchEvent++in++motionEvent:down
I/sxd: TouchTestRelativeLayout--onTouchEvent++out++ret:true,motionEvent:down
I/sxd: TouchTestRelativeLayout--dispatchTouchEvent++out++ret:true,motionEvent:down
I/sxd: TouchTestRelativeLayout--dispatchTouchEvent++in++motionEvent:move
I/sxd: TouchTestRelativeLayout--onTouch++ret:false,motionEvent:move
I/sxd: TouchTestRelativeLayout--onTouchEvent++in++motionEvent:move
I/sxd: TouchTestRelativeLayout--onTouchEvent++out++ret:true,motionEvent:move
I/sxd: TouchTestRelativeLayout--dispatchTouchEvent++out++ret:true,motionEvent:move
I/sxd: TouchTestRelativeLayout--dispatchTouchEvent++in++motionEvent:up
I/sxd: TouchTestRelativeLayout--onTouch++ret:false,motionEvent:up
I/sxd: TouchTestRelativeLayout--onTouchEvent++in++motionEvent:up
I/sxd: TouchTestRelativeLayout--onTouchEvent++out++ret:true,motionEvent:up
I/sxd: TouchTestRelativeLayout--dispatchTouchEvent++out++ret:true,motionEvent:up
I/sxd: TouchTestRelativeLayout--onClick
結果表明,當視圖沒有消費down事件,則其後的所有事件都不會再分發給它,但會觸發一次該視圖的onTouch方法。
事件被消費後,不再上傳
只要視圖消費了down事件,則不管其後續事件是否消費,只要不被攔截,則所有事件都會被分發到這裏。且被下層消費了的事件,不會再被上層消費。
示例代碼:
onTouchEvent未消費Move事件
@Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
String motionEvent = null;
switch (action) {
case MotionEvent.ACTION_DOWN:
motionEvent = "down";
break;
case MotionEvent.ACTION_MOVE:
motionEvent = "move";
return false;
case MotionEvent.ACTION_UP:
motionEvent = "up";
break;
case MotionEvent.ACTION_CANCEL:
motionEvent = "cancel";
break;
}
Log.i(SXD, TAG + "--onTouchEvent++in++motionEvent:" + motionEvent);
boolean ret = super.onTouchEvent(event);
Log.i(SXD, TAG + "--onTouchEvent++out++ret:" + ret + ",motionEvent:" + motionEvent);
return ret;
}
I/sxd: TouchTestRelativeLayout--dispatchTouchEvent++in++motionEvent:down
I/sxd: TouchTestRelativeLayout--onInterceptTouchEvent++in++motionEvent:down
I/sxd: TouchTestRelativeLayout--onInterceptTouchEvent++out++ret:false,motionEvent:down
I/sxd: TouchButton--dispatchTouchEvent++in++motionEvent:down
I/sxd: TouchButton--onTouch++ret:false,motionEvent:down
I/sxd: TouchButton--onTouchEvent++in++motionEvent:down
I/sxd: TouchButton--onTouchEvent++out++ret:true,motionEvent:down
I/sxd: TouchButton--dispatchTouchEvent++out++ret:true,motionEvent:down
I/sxd: TouchTestRelativeLayout--dispatchTouchEvent++out++ret:true,motionEvent:down
I/sxd: TouchTestRelativeLayout--dispatchTouchEvent++in++motionEvent:move
I/sxd: TouchTestRelativeLayout--onInterceptTouchEvent++in++motionEvent:move
I/sxd: TouchTestRelativeLayout--onInterceptTouchEvent++out++ret:false,motionEvent:move
I/sxd: TouchButton--dispatchTouchEvent++in++motionEvent:move
I/sxd: TouchButton--onTouch++ret:false,motionEvent:move
I/sxd: TouchButton--dispatchTouchEvent++out++ret:false,motionEvent:move
I/sxd: TouchTestRelativeLayout--dispatchTouchEvent++out++ret:false,motionEvent:move
I/sxd: TouchTestRelativeLayout--dispatchTouchEvent++in++motionEvent:move
I/sxd: TouchTestRelativeLayout--onInterceptTouchEvent++in++motionEvent:move
I/sxd: TouchTestRelativeLayout--onInterceptTouchEvent++out++ret:false,motionEvent:move
I/sxd: TouchButton--dispatchTouchEvent++in++motionEvent:move
I/sxd: TouchButton--onTouch++ret:false,motionEvent:move
I/sxd: TouchButton--dispatchTouchEvent++out++ret:false,motionEvent:move
I/sxd: TouchTestRelativeLayout--dispatchTouchEvent++out++ret:false,motionEvent:move
I/sxd: TouchTestRelativeLayout--dispatchTouchEvent++in++motionEvent:up
I/sxd: TouchTestRelativeLayout--onInterceptTouchEvent++in++motionEvent:up
I/sxd: TouchTestRelativeLayout--onInterceptTouchEvent++out++ret:false,motionEvent:up
I/sxd: TouchButton--dispatchTouchEvent++in++motionEvent:up
I/sxd: TouchButton--onTouch++ret:false,motionEvent:up
I/sxd: TouchButton--onTouchEvent++in++motionEvent:up
I/sxd: TouchButton--onTouchEvent++out++ret:true,motionEvent:up
I/sxd: TouchButton--dispatchTouchEvent++out++ret:true,motionEvent:up
I/sxd: TouchTestRelativeLayout--dispatchTouchEvent++out++ret:true,motionEvent:up
I/sxd: TouchButton--onClick
結果表明,只要視圖消費掉了down事件,且上層分發不再攔截,則其後的所有事件都會再分發給它,且其消費不對後續事件消費時,僅會觸發其當前事件的onTouch
方法。
onTouchEvent未消費Up事件
@Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
String motionEvent = null;
switch (action) {
case MotionEvent.ACTION_DOWN:
motionEvent = "down";
break;
case MotionEvent.ACTION_MOVE:
motionEvent = "move";
break;
case MotionEvent.ACTION_UP:
motionEvent = "up";
return false;
case MotionEvent.ACTION_CANCEL:
motionEvent = "cancel";
break;
}
Log.i(SXD, TAG + "--onTouchEvent++in++motionEvent:" + motionEvent);
boolean ret = super.onTouchEvent(event);
Log.i(SXD, TAG + "--onTouchEvent++out++ret:" + ret + ",motionEvent:" + motionEvent);
return ret;
}
I/sxd: TouchTestRelativeLayout--dispatchTouchEvent++in++motionEvent:down
I/sxd: TouchTestRelativeLayout--onInterceptTouchEvent++in++motionEvent:down
I/sxd: TouchTestRelativeLayout--onInterceptTouchEvent++out++ret:false,motionEvent:down
I/sxd: TouchButton--dispatchTouchEvent++in++motionEvent:down
I/sxd: TouchButton--onTouch++ret:false,motionEvent:down
I/sxd: TouchButton--onTouchEvent++in++motionEvent:down
I/sxd: TouchButton--onTouchEvent++out++ret:true,motionEvent:down
I/sxd: TouchButton--dispatchTouchEvent++out++ret:true,motionEvent:down
I/sxd: TouchTestRelativeLayout--dispatchTouchEvent++out++ret:true,motionEvent:down
I/sxd: TouchTestRelativeLayout--dispatchTouchEvent++in++motionEvent:move
I/sxd: TouchTestRelativeLayout--onInterceptTouchEvent++in++motionEvent:move
I/sxd: TouchTestRelativeLayout--onInterceptTouchEvent++out++ret:false,motionEvent:move
I/sxd: TouchButton--dispatchTouchEvent++in++motionEvent:move
I/sxd: TouchButton--onTouch++ret:false,motionEvent:move
I/sxd: TouchButton--onTouchEvent++in++motionEvent:move
I/sxd: TouchButton--onTouchEvent++out++ret:true,motionEvent:move
I/sxd: TouchButton--dispatchTouchEvent++out++ret:true,motionEvent:move
I/sxd: TouchTestRelativeLayout--dispatchTouchEvent++out++ret:true,motionEvent:move
I/sxd: TouchTestRelativeLayout--dispatchTouchEvent++in++motionEvent:up
I/sxd: TouchTestRelativeLayout--onInterceptTouchEvent++in++motionEvent:up
I/sxd: TouchTestRelativeLayout--onInterceptTouchEvent++out++ret:false,motionEvent:up
I/sxd: TouchButton--dispatchTouchEvent++in++motionEvent:up
I/sxd: TouchButton--onTouch++ret:false,motionEvent:up
I/sxd: TouchButton--dispatchTouchEvent++out++ret:false,motionEvent:up
I/sxd: TouchTestRelativeLayout--dispatchTouchEvent++out++ret:false,motionEvent:up
結果表明,只要視圖消費掉了down事件,且上層分發不再攔截,則其後的所有事件都會再分發給它,且其消費不對後續事件消費時,僅會觸發其當前事件的onTouch
方法。