• 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事件或已存在之前消费过事件的对象时,需要通过disallowInterceptonInterceptTouchEvent(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的dispatchTouchEventonInterceptTouchEventonTouchEvent方法或View的dispatchTouchEventonTouchEvent方法,来将事件更深层的分发和消费,这些方法的处理结果,又逐级回传,从而进一步影响后面事件的分发和消费方式。
  接下来,以上面的源码分析为基础,具体分析触摸事件分发处理情况。

各种触摸事件分发、消费情况详解

ACTION_DOWN事件未被消费,分发失败

  在一个触摸事件中,down事件起着至关重要的作用,down事件就是“敲门”事件,如果门没有敲开,后面的事件也就没有意义了,所以如果down事件没有被分发出去,即没有被消费,那么后面的事件也就不会被分发了。
Android触摸事件分发机制详解_第1张图片

说明:

  • 都不拦截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事件,则之后的事件不会再向其分发。
Android触摸事件分发机制详解_第2张图片

示例程序:

初始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事件,则不管其后续事件是否消费,只要不被拦截,则所有事件都会被分发到这里。且被下层消费了的事件,不会再被上层消费。
Android触摸事件分发机制详解_第3张图片

示例代码:

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方法。

更多相关文章

  1. Android 比Timer更好方法
  2. Android中AppWidget使用方法
  3. Android 官方 Lambda支持方法
  4. android BroadcastReceiver遇到 java.lang.IllegalAccessExcepti
  5. Android 调用系统相机拍照保存以及调用系统相册的方法
  6. Android软键盘弹出时不把布局顶上去的解决方法
  7. Android全屏(包含3种隐藏顶部状态栏及标题栏和一种隐藏Android 4.
  8. Android两种播放视频的方法(SurfaceView、MediaPlayer、SeekBar)
  9. Android 4.0 HttpUrlConnection的getInputStream()方法总是返回

随机推荐

  1. Android TextView全属性
  2. Android中对Handle机制的理解
  3. android 中的 Broadcast 机制详解
  4. Android Studio开发基础之AutoCompleteTe
  5. Android应用程序用真机调试步骤
  6. Android Studio系列(二)使用Android Studio
  7. android 设置布局动画
  8. Android 2.0 平台的亮点
  9. Android(安卓)如何保持屏幕长亮?
  10. Android开发从入门到精通(项目案例版)——