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
方法,来将事件更深层的分发和消费,这些方法的处理结果,又逐级回传,从而进一步影响后面事件的分发和消费方式。
接下来,以上面的源码分析为基础,具体分析触摸事件分发处理情况。
在一个触摸事件中,down事件起着至关重要的作用,down事件就是“敲门”事件,如果门没有敲开,后面的事件也就没有意义了,所以如果down事件没有被分发出去,即没有被消费,那么后面的事件也就不会被分发了。
说明:
onTouchEvent
会依次向上传递dispatchTouchEvent
会返回false,表示事件没有被派发出去示例程序:
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事件没有分发成功后,在没有后续事件进行分发。
如果视图消费了down事件,且之后有事件被拦截,则每种被拦截事件类型(move,up等)都会触发收到一次cancel事件。
示例程序:
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等事件可被拦截层消费。
@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事件不会被拦截层消费。
示例程序:
@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
@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事件,则不管其后续事件是否消费,只要不被拦截,则所有事件都会被分发到这里。且被下层消费了的事件,不会再被上层消费。
示例代码:
@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
方法。
@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
方法。