Android触摸事件分发机制详解
来源:互联网 发布:买家淘宝定制商品 编辑:程序博客网 时间:2024/05/17 03:04
- 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:downI/sxd: TouchTestRelativeLayout--onInterceptTouchEvent++in++motionEvent:downI/sxd: TouchTestRelativeLayout--onInterceptTouchEvent++out++ret:false,motionEvent:downI/sxd: TouchButton--dispatchTouchEvent++in++motionEvent:downI/sxd: TouchButton--onTouch++ret:false,motionEvent:downI/sxd: TouchButton--onTouchEvent++in++motionEvent:downI/sxd: TouchButton--onTouchEvent++out++ret:true,motionEvent:downI/sxd: TouchButton--dispatchTouchEvent++out++ret:true,motionEvent:downI/sxd: TouchTestRelativeLayout--dispatchTouchEvent++out++ret:true,motionEvent:downI/sxd: TouchTestRelativeLayout--dispatchTouchEvent++in++motionEvent:moveI/sxd: TouchButton--dispatchTouchEvent++in++motionEvent:cancelI/sxd: TouchButton--onTouch++ret:false,motionEvent:cancelI/sxd: TouchButton--onTouchEvent++in++motionEvent:cancelI/sxd: TouchButton--onTouchEvent++out++ret:true,motionEvent:cancelI/sxd: TouchButton--dispatchTouchEvent++out++ret:true,motionEvent:cancelI/sxd: TouchTestRelativeLayout--dispatchTouchEvent++out++ret:true,motionEvent:moveI/sxd: TouchTestRelativeLayout--dispatchTouchEvent++in++motionEvent:moveI/sxd: TouchTestRelativeLayout--onTouch++ret:false,motionEvent:moveI/sxd: TouchTestRelativeLayout--onTouchEvent++in++motionEvent:moveI/sxd: TouchTestRelativeLayout--onTouchEvent++out++ret:true,motionEvent:moveI/sxd: TouchTestRelativeLayout--dispatchTouchEvent++out++ret:true,motionEvent:moveI/sxd: TouchTestRelativeLayout--dispatchTouchEvent++in++motionEvent:moveI/sxd: TouchTestRelativeLayout--onTouch++ret:false,motionEvent:moveI/sxd: TouchTestRelativeLayout--onTouchEvent++in++motionEvent:moveI/sxd: TouchTestRelativeLayout--onTouchEvent++out++ret:true,motionEvent:moveI/sxd: TouchTestRelativeLayout--dispatchTouchEvent++out++ret:true,motionEvent:moveI/sxd: TouchTestRelativeLayout--dispatchTouchEvent++in++motionEvent:upI/sxd: TouchTestRelativeLayout--onTouch++ret:false,motionEvent:upI/sxd: TouchTestRelativeLayout--onTouchEvent++in++motionEvent:upI/sxd: TouchTestRelativeLayout--onTouchEvent++out++ret:true,motionEvent:upI/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:downI/sxd: TouchTestRelativeLayout--onInterceptTouchEvent++in++motionEvent:downI/sxd: TouchTestRelativeLayout--onInterceptTouchEvent++out++ret:false,motionEvent:downI/sxd: TouchButton--dispatchTouchEvent++in++motionEvent:downI/sxd: TouchButton--onTouch++ret:false,motionEvent:downI/sxd: TouchButton--onTouchEvent++in++motionEvent:downI/sxd: TouchButton--onTouchEvent++out++ret:true,motionEvent:downI/sxd: TouchButton--dispatchTouchEvent++out++ret:true,motionEvent:downI/sxd: TouchTestRelativeLayout--dispatchTouchEvent++out++ret:true,motionEvent:downI/sxd: TouchTestRelativeLayout--dispatchTouchEvent++in++motionEvent:moveI/sxd: TouchTestRelativeLayout--onInterceptTouchEvent++in++motionEvent:moveI/sxd: TouchTestRelativeLayout--onInterceptTouchEvent++out++ret:false,motionEvent:moveI/sxd: TouchButton--dispatchTouchEvent++in++motionEvent:moveI/sxd: TouchButton--onTouch++ret:false,motionEvent:moveI/sxd: TouchButton--onTouchEvent++in++motionEvent:moveI/sxd: TouchButton--onTouchEvent++out++ret:true,motionEvent:moveI/sxd: TouchButton--dispatchTouchEvent++out++ret:true,motionEvent:moveI/sxd: TouchTestRelativeLayout--dispatchTouchEvent++out++ret:true,motionEvent:moveI/sxd: TouchTestRelativeLayout--dispatchTouchEvent++in++motionEvent:moveI/sxd: TouchTestRelativeLayout--onInterceptTouchEvent++in++motionEvent:moveI/sxd: TouchTestRelativeLayout--onInterceptTouchEvent++out++ret:false,motionEvent:moveI/sxd: TouchButton--dispatchTouchEvent++in++motionEvent:moveI/sxd: TouchButton--onTouch++ret:false,motionEvent:moveI/sxd: TouchButton--onTouchEvent++in++motionEvent:moveI/sxd: TouchButton--onTouchEvent++out++ret:true,motionEvent:moveI/sxd: TouchButton--dispatchTouchEvent++out++ret:true,motionEvent:moveI/sxd: TouchTestRelativeLayout--dispatchTouchEvent++out++ret:true,motionEvent:moveI/sxd: TouchTestRelativeLayout--dispatchTouchEvent++in++motionEvent:moveI/sxd: TouchTestRelativeLayout--onInterceptTouchEvent++in++motionEvent:moveI/sxd: TouchTestRelativeLayout--onInterceptTouchEvent++out++ret:false,motionEvent:moveI/sxd: TouchButton--dispatchTouchEvent++in++motionEvent:moveI/sxd: TouchButton--onTouch++ret:false,motionEvent:moveI/sxd: TouchButton--onTouchEvent++in++motionEvent:moveI/sxd: TouchButton--onTouchEvent++out++ret:true,motionEvent:moveI/sxd: TouchButton--dispatchTouchEvent++out++ret:true,motionEvent:moveI/sxd: TouchTestRelativeLayout--dispatchTouchEvent++out++ret:true,motionEvent:moveI/sxd: TouchTestRelativeLayout--dispatchTouchEvent++in++motionEvent:upI/sxd: TouchButton--dispatchTouchEvent++in++motionEvent:cancelI/sxd: TouchButton--onTouch++ret:false,motionEvent:cancelI/sxd: TouchButton--onTouchEvent++in++motionEvent:cancelI/sxd: TouchButton--onTouchEvent++out++ret:true,motionEvent:cancelI/sxd: TouchButton--dispatchTouchEvent++out++ret:true,motionEvent:cancelI/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:downI/sxd: TouchTestRelativeLayout--onTouch++ret:false,motionEvent:downI/sxd: TouchTestRelativeLayout--onTouchEvent++in++motionEvent:downI/sxd: TouchTestRelativeLayout--onTouchEvent++out++ret:true,motionEvent:downI/sxd: TouchTestRelativeLayout--dispatchTouchEvent++out++ret:true,motionEvent:downI/sxd: TouchTestRelativeLayout--dispatchTouchEvent++in++motionEvent:moveI/sxd: TouchTestRelativeLayout--onTouch++ret:false,motionEvent:moveI/sxd: TouchTestRelativeLayout--onTouchEvent++in++motionEvent:moveI/sxd: TouchTestRelativeLayout--onTouchEvent++out++ret:true,motionEvent:moveI/sxd: TouchTestRelativeLayout--dispatchTouchEvent++out++ret:true,motionEvent:moveI/sxd: TouchTestRelativeLayout--dispatchTouchEvent++in++motionEvent:moveI/sxd: TouchTestRelativeLayout--onTouch++ret:false,motionEvent:moveI/sxd: TouchTestRelativeLayout--onTouchEvent++in++motionEvent:moveI/sxd: TouchTestRelativeLayout--onTouchEvent++out++ret:true,motionEvent:moveI/sxd: TouchTestRelativeLayout--dispatchTouchEvent++out++ret:true,motionEvent:moveI/sxd: TouchTestRelativeLayout--dispatchTouchEvent++in++motionEvent:upI/sxd: TouchTestRelativeLayout--onTouch++ret:false,motionEvent:upI/sxd: TouchTestRelativeLayout--onTouchEvent++in++motionEvent:upI/sxd: TouchTestRelativeLayout--onTouchEvent++out++ret:true,motionEvent:upI/sxd: TouchTestRelativeLayout--dispatchTouchEvent++out++ret:true,motionEvent:upI/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:downI/sxd: TouchTestRelativeLayout--onInterceptTouchEvent++in++motionEvent:downI/sxd: TouchTestRelativeLayout--onInterceptTouchEvent++out++ret:false,motionEvent:downI/sxd: TouchButton--dispatchTouchEvent++in++motionEvent:downI/sxd: TouchButton--onTouch++ret:false,motionEvent:downI/sxd: TouchButton--dispatchTouchEvent++out++ret:false,motionEvent:downI/sxd: TouchTestRelativeLayout--onTouch++ret:false,motionEvent:downI/sxd: TouchTestRelativeLayout--onTouchEvent++in++motionEvent:downI/sxd: TouchTestRelativeLayout--onTouchEvent++out++ret:true,motionEvent:downI/sxd: TouchTestRelativeLayout--dispatchTouchEvent++out++ret:true,motionEvent:downI/sxd: TouchTestRelativeLayout--dispatchTouchEvent++in++motionEvent:moveI/sxd: TouchTestRelativeLayout--onTouch++ret:false,motionEvent:moveI/sxd: TouchTestRelativeLayout--onTouchEvent++in++motionEvent:moveI/sxd: TouchTestRelativeLayout--onTouchEvent++out++ret:true,motionEvent:moveI/sxd: TouchTestRelativeLayout--dispatchTouchEvent++out++ret:true,motionEvent:moveI/sxd: TouchTestRelativeLayout--dispatchTouchEvent++in++motionEvent:upI/sxd: TouchTestRelativeLayout--onTouch++ret:false,motionEvent:upI/sxd: TouchTestRelativeLayout--onTouchEvent++in++motionEvent:upI/sxd: TouchTestRelativeLayout--onTouchEvent++out++ret:true,motionEvent:upI/sxd: TouchTestRelativeLayout--dispatchTouchEvent++out++ret:true,motionEvent:upI/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:downI/sxd: TouchTestRelativeLayout--onInterceptTouchEvent++in++motionEvent:downI/sxd: TouchTestRelativeLayout--onInterceptTouchEvent++out++ret:false,motionEvent:downI/sxd: TouchButton--dispatchTouchEvent++in++motionEvent:downI/sxd: TouchButton--onTouch++ret:false,motionEvent:downI/sxd: TouchButton--onTouchEvent++in++motionEvent:downI/sxd: TouchButton--onTouchEvent++out++ret:true,motionEvent:downI/sxd: TouchButton--dispatchTouchEvent++out++ret:true,motionEvent:downI/sxd: TouchTestRelativeLayout--dispatchTouchEvent++out++ret:true,motionEvent:downI/sxd: TouchTestRelativeLayout--dispatchTouchEvent++in++motionEvent:moveI/sxd: TouchTestRelativeLayout--onInterceptTouchEvent++in++motionEvent:moveI/sxd: TouchTestRelativeLayout--onInterceptTouchEvent++out++ret:false,motionEvent:moveI/sxd: TouchButton--dispatchTouchEvent++in++motionEvent:moveI/sxd: TouchButton--onTouch++ret:false,motionEvent:moveI/sxd: TouchButton--dispatchTouchEvent++out++ret:false,motionEvent:moveI/sxd: TouchTestRelativeLayout--dispatchTouchEvent++out++ret:false,motionEvent:moveI/sxd: TouchTestRelativeLayout--dispatchTouchEvent++in++motionEvent:moveI/sxd: TouchTestRelativeLayout--onInterceptTouchEvent++in++motionEvent:moveI/sxd: TouchTestRelativeLayout--onInterceptTouchEvent++out++ret:false,motionEvent:moveI/sxd: TouchButton--dispatchTouchEvent++in++motionEvent:moveI/sxd: TouchButton--onTouch++ret:false,motionEvent:moveI/sxd: TouchButton--dispatchTouchEvent++out++ret:false,motionEvent:moveI/sxd: TouchTestRelativeLayout--dispatchTouchEvent++out++ret:false,motionEvent:moveI/sxd: TouchTestRelativeLayout--dispatchTouchEvent++in++motionEvent:upI/sxd: TouchTestRelativeLayout--onInterceptTouchEvent++in++motionEvent:upI/sxd: TouchTestRelativeLayout--onInterceptTouchEvent++out++ret:false,motionEvent:upI/sxd: TouchButton--dispatchTouchEvent++in++motionEvent:upI/sxd: TouchButton--onTouch++ret:false,motionEvent:upI/sxd: TouchButton--onTouchEvent++in++motionEvent:upI/sxd: TouchButton--onTouchEvent++out++ret:true,motionEvent:upI/sxd: TouchButton--dispatchTouchEvent++out++ret:true,motionEvent:upI/sxd: TouchTestRelativeLayout--dispatchTouchEvent++out++ret:true,motionEvent:upI/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:downI/sxd: TouchTestRelativeLayout--onInterceptTouchEvent++in++motionEvent:downI/sxd: TouchTestRelativeLayout--onInterceptTouchEvent++out++ret:false,motionEvent:downI/sxd: TouchButton--dispatchTouchEvent++in++motionEvent:downI/sxd: TouchButton--onTouch++ret:false,motionEvent:downI/sxd: TouchButton--onTouchEvent++in++motionEvent:downI/sxd: TouchButton--onTouchEvent++out++ret:true,motionEvent:downI/sxd: TouchButton--dispatchTouchEvent++out++ret:true,motionEvent:downI/sxd: TouchTestRelativeLayout--dispatchTouchEvent++out++ret:true,motionEvent:downI/sxd: TouchTestRelativeLayout--dispatchTouchEvent++in++motionEvent:moveI/sxd: TouchTestRelativeLayout--onInterceptTouchEvent++in++motionEvent:moveI/sxd: TouchTestRelativeLayout--onInterceptTouchEvent++out++ret:false,motionEvent:moveI/sxd: TouchButton--dispatchTouchEvent++in++motionEvent:moveI/sxd: TouchButton--onTouch++ret:false,motionEvent:moveI/sxd: TouchButton--onTouchEvent++in++motionEvent:moveI/sxd: TouchButton--onTouchEvent++out++ret:true,motionEvent:moveI/sxd: TouchButton--dispatchTouchEvent++out++ret:true,motionEvent:moveI/sxd: TouchTestRelativeLayout--dispatchTouchEvent++out++ret:true,motionEvent:moveI/sxd: TouchTestRelativeLayout--dispatchTouchEvent++in++motionEvent:upI/sxd: TouchTestRelativeLayout--onInterceptTouchEvent++in++motionEvent:upI/sxd: TouchTestRelativeLayout--onInterceptTouchEvent++out++ret:false,motionEvent:upI/sxd: TouchButton--dispatchTouchEvent++in++motionEvent:upI/sxd: TouchButton--onTouch++ret:false,motionEvent:upI/sxd: TouchButton--dispatchTouchEvent++out++ret:false,motionEvent:upI/sxd: TouchTestRelativeLayout--dispatchTouchEvent++out++ret:false,motionEvent:up
结果表明,只要视图消费掉了down事件,且上层分发不再拦截,则其后的所有事件都会再分发给它,且其消费不对后续事件消费时,仅会触发其当前事件的onTouch
方法。
- Android触摸事件分发机制详解
- 触摸事件分发机制详解
- Android触摸事件分发机制
- Android触摸事件分发机制
- Android触摸事件分发机制
- Android触摸事件分发机制
- Android触摸事件分发机制
- Android触摸事件分发机制
- Android触摸事件分发机制
- Android触摸事件分发机制
- android触摸事件分发机制
- android触摸事件分发机制
- Android触摸事件分发机制
- Android触摸事件分发机制
- Android触摸事件分发机制
- android 触摸事件分发机制
- Android 触摸事件分发传递机制
- Android中触摸事件传递分发机制
- Haproxy+Keepalived搭建Weblogic高可用负载均衡集群
- python 开发环境搭建
- CSS3 布局样式
- listview嵌套listview,子listview只显示一个item问题
- git 使用笔记
- Android触摸事件分发机制详解
- 友盟统计平台crash信息UUID与打包时生成.dSYM的UUID不一致
- 浅谈萃取技术
- bootstrap3学习笔记1-排版
- hdu4734 F(x)(数位dp)
- IT忍者神龟之Javascript开发工具
- Native App、Web App、Hybrid App的区别
- 利用Three.js构建粒子系统
- UML 回顾与复习