当前位置: 代码迷 >> Android >> 初学者进阶之Android Touch事件传递(四)
  详细解决方案

初学者进阶之Android Touch事件传递(四)

热度:76   发布时间:2016-04-28 02:00:28.0
菜鸟进阶之Android Touch事件传递(四)

尊重他人劳动成果,转载请说明出处:http://blog.csdn.net/bingospunky/article/details/44278533

在该系列文章第四篇,我准备介绍一下viewpager的touch事件处理。

如果想了解touch和click的那些事,请浏览touch事件传递系列的第一篇http://blog.csdn.net/bingospunky/article/details/43603397

如果想了解touch事件一步一步传递的路线,请浏览touch事件传递系列的第二篇http://blog.csdn.net/bingospunky/article/details/43735497

如果想从源码角度什么理解viewgroup的dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent如何实现,请浏览touch事件传递系列的第二篇http://blog.csdn.net/bingospunky/article/details/44156771

源码

代码A:boolean android.support.v4.view.ViewPager.onInterceptTouchEvent(MotionEvent ev)

public boolean onInterceptTouchEvent(MotionEvent ev) {        /*         * This method JUST determines whether we want to intercept the motion.         * If we return true, onMotionEvent will be called and we do the actual         * scrolling there.         */        final int action = ev.getAction() & MotionEventCompat.ACTION_MASK;        // Always take care of the touch gesture being complete.        if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {            // Release the drag.            if (DEBUG) Log.v(TAG, "Intercept done!");            mIsBeingDragged = false;            mIsUnableToDrag = false;            mActivePointerId = INVALID_POINTER;            if (mVelocityTracker != null) {                mVelocityTracker.recycle();                mVelocityTracker = null;            }            return false;        }        // Nothing more to do here if we have decided whether or not we        // are dragging.        if (action != MotionEvent.ACTION_DOWN) {            if (mIsBeingDragged) {                if (DEBUG) Log.v(TAG, "Intercept returning true!");                return true;            }            if (mIsUnableToDrag) {                if (DEBUG) Log.v(TAG, "Intercept returning false!");                return false;            }        }        switch (action) {            case MotionEvent.ACTION_MOVE: {                /*                 * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check                 * whether the user has moved far enough from his original down touch.                 */                /*                * Locally do absolute value. mLastMotionY is set to the y value                * of the down event.                */                final int activePointerId = mActivePointerId;                if (activePointerId == INVALID_POINTER) {                    // If we don't have a valid id, the touch down wasn't on content.                    break;                }                final int pointerIndex = MotionEventCompat.findPointerIndex(ev, activePointerId);                final float x = MotionEventCompat.getX(ev, pointerIndex);                final float dx = x - mLastMotionX;                final float xDiff = Math.abs(dx);                final float y = MotionEventCompat.getY(ev, pointerIndex);                final float yDiff = Math.abs(y - mInitialMotionY);                if (DEBUG) Log.v(TAG, "Moved x to " + x + "," + y + " diff=" + xDiff + "," + yDiff);                if (dx != 0 && !isGutterDrag(mLastMotionX, dx) &&                        canScroll(this, false, (int) dx, (int) x, (int) y)) {                    // Nested view has scrollable area under this point. Let it be handled there.                    mLastMotionX = x;                    mLastMotionY = y;                    mIsUnableToDrag = true;                    return false;                }                if (xDiff > mTouchSlop && xDiff * 0.5f > yDiff) {                    if (DEBUG) Log.v(TAG, "Starting drag!");                    mIsBeingDragged = true;                    requestParentDisallowInterceptTouchEvent(true);                    setScrollState(SCROLL_STATE_DRAGGING);                    mLastMotionX = dx > 0 ? mInitialMotionX + mTouchSlop :                            mInitialMotionX - mTouchSlop;                    mLastMotionY = y;                    setScrollingCacheEnabled(true);                } else if (yDiff > mTouchSlop) {                    // The finger has moved enough in the vertical                    // direction to be counted as a drag...  abort                    // any attempt to drag horizontally, to work correctly                    // with children that have scrolling containers.                    if (DEBUG) Log.v(TAG, "Starting unable to drag!");                    mIsUnableToDrag = true;                }                if (mIsBeingDragged) {                    // Scroll to follow the motion event                    if (performDrag(x)) {                        ViewCompat.postInvalidateOnAnimation(this);                    }                }                break;            }            case MotionEvent.ACTION_DOWN: {                /*                 * Remember location of down touch.                 * ACTION_DOWN always refers to pointer index 0.                 */                mLastMotionX = mInitialMotionX = ev.getX();                mLastMotionY = mInitialMotionY = ev.getY();                mActivePointerId = MotionEventCompat.getPointerId(ev, 0);                mIsUnableToDrag = false;                mScroller.computeScrollOffset();                if (mScrollState == SCROLL_STATE_SETTLING &&                        Math.abs(mScroller.getFinalX() - mScroller.getCurrX()) > mCloseEnough) {                    // Let the user 'catch' the pager as it animates.                    mScroller.abortAnimation();                    mPopulatePending = false;                    populate();                    mIsBeingDragged = true;                    requestParentDisallowInterceptTouchEvent(true);                    setScrollState(SCROLL_STATE_DRAGGING);                } else {                    completeScroll(false);                    mIsBeingDragged = false;                }                if (DEBUG) Log.v(TAG, "Down at " + mLastMotionX + "," + mLastMotionY                        + " mIsBeingDragged=" + mIsBeingDragged                        + "mIsUnableToDrag=" + mIsUnableToDrag);                break;            }            case MotionEventCompat.ACTION_POINTER_UP:                onSecondaryPointerUp(ev);                break;        }        if (mVelocityTracker == null) {            mVelocityTracker = VelocityTracker.obtain();        }        mVelocityTracker.addMovement(ev);        /*         * The only time we want to intercept motion events is if we are in the         * drag mode.         */        return mIsBeingDragged;    }
代码B:boolean android.support.v4.view.ViewPager.onTouchEvent(MotionEvent ev)

public boolean onTouchEvent(MotionEvent ev) {        if (mFakeDragging) {            // A fake drag is in progress already, ignore this real one            // but still eat the touch events.            // (It is likely that the user is multi-touching the screen.)            return true;        }        if (ev.getAction() == MotionEvent.ACTION_DOWN && ev.getEdgeFlags() != 0) {            // Don't handle edge touches immediately -- they may actually belong to one of our            // descendants.            return false;        }        if (mAdapter == null || mAdapter.getCount() == 0) {            // Nothing to present or scroll; nothing to touch.            return false;        }        if (mVelocityTracker == null) {            mVelocityTracker = VelocityTracker.obtain();        }        mVelocityTracker.addMovement(ev);        final int action = ev.getAction();        boolean needsInvalidate = false;        switch (action & MotionEventCompat.ACTION_MASK) {            case MotionEvent.ACTION_DOWN: {                mScroller.abortAnimation();                mPopulatePending = false;                populate();                // Remember where the motion event started                mLastMotionX = mInitialMotionX = ev.getX();                mLastMotionY = mInitialMotionY = ev.getY();                mActivePointerId = MotionEventCompat.getPointerId(ev, 0);                break;            }            case MotionEvent.ACTION_MOVE:                if (!mIsBeingDragged) {                    final int pointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId);                    final float x = MotionEventCompat.getX(ev, pointerIndex);                    final float xDiff = Math.abs(x - mLastMotionX);                    final float y = MotionEventCompat.getY(ev, pointerIndex);                    final float yDiff = Math.abs(y - mLastMotionY);                    if (DEBUG) Log.v(TAG, "Moved x to " + x + "," + y + " diff=" + xDiff + "," + yDiff);                    if (xDiff > mTouchSlop && xDiff > yDiff) {                        if (DEBUG) Log.v(TAG, "Starting drag!");                        mIsBeingDragged = true;                        requestParentDisallowInterceptTouchEvent(true);                        mLastMotionX = x - mInitialMotionX > 0 ? mInitialMotionX + mTouchSlop :                                mInitialMotionX - mTouchSlop;                        mLastMotionY = y;                        setScrollState(SCROLL_STATE_DRAGGING);                        setScrollingCacheEnabled(true);                        // Disallow Parent Intercept, just in case                        ViewParent parent = getParent();                        if (parent != null) {                            parent.requestDisallowInterceptTouchEvent(true);                        }                    }                }                // Not else! Note that mIsBeingDragged can be set above.                if (mIsBeingDragged) {                    // Scroll to follow the motion event                    final int activePointerIndex = MotionEventCompat.findPointerIndex(                            ev, mActivePointerId);                    final float x = MotionEventCompat.getX(ev, activePointerIndex);                    needsInvalidate |= performDrag(x);                }                break;            case MotionEvent.ACTION_UP:                if (mIsBeingDragged) {                    final VelocityTracker velocityTracker = mVelocityTracker;                    velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);                    int initialVelocity = (int) VelocityTrackerCompat.getXVelocity(                            velocityTracker, mActivePointerId);                    mPopulatePending = true;                    final int width = getClientWidth();                    final int scrollX = getScrollX();                    final ItemInfo ii = infoForCurrentScrollPosition();                    final int currentPage = ii.position;                    final float pageOffset = (((float) scrollX / width) - ii.offset) / ii.widthFactor;                    final int activePointerIndex =                            MotionEventCompat.findPointerIndex(ev, mActivePointerId);                    final float x = MotionEventCompat.getX(ev, activePointerIndex);                    final int totalDelta = (int) (x - mInitialMotionX);                    int nextPage = determineTargetPage(currentPage, pageOffset, initialVelocity,                            totalDelta);                    setCurrentItemInternal(nextPage, true, true, initialVelocity);                    mActivePointerId = INVALID_POINTER;                    endDrag();                    needsInvalidate = mLeftEdge.onRelease() | mRightEdge.onRelease();                }                break;            case MotionEvent.ACTION_CANCEL:                if (mIsBeingDragged) {                    scrollToItem(mCurItem, true, 0, false);                    mActivePointerId = INVALID_POINTER;                    endDrag();                    needsInvalidate = mLeftEdge.onRelease() | mRightEdge.onRelease();                }                break;            case MotionEventCompat.ACTION_POINTER_DOWN: {                final int index = MotionEventCompat.getActionIndex(ev);                final float x = MotionEventCompat.getX(ev, index);                mLastMotionX = x;                mActivePointerId = MotionEventCompat.getPointerId(ev, index);                break;            }            case MotionEventCompat.ACTION_POINTER_UP:                onSecondaryPointerUp(ev);                mLastMotionX = MotionEventCompat.getX(ev,                        MotionEventCompat.findPointerIndex(ev, mActivePointerId));                break;        }        if (needsInvalidate) {            ViewCompat.postInvalidateOnAnimation(this);        }        return true;    }
代码C:void android.support.v4.view.ViewPager.onSecondaryPointerUp(MotionEvent ev)

private void onSecondaryPointerUp(MotionEvent ev) {        final int pointerIndex = MotionEventCompat.getActionIndex(ev);        final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex);        if (pointerId == mActivePointerId) {            // This was our active pointer going up. Choose a new            // active pointer and adjust accordingly.            final int newPointerIndex = pointerIndex == 0 ? 1 : 0;            mLastMotionX = MotionEventCompat.getX(ev, newPointerIndex);            mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex);            if (mVelocityTracker != null) {                mVelocityTracker.clear();            }        }    }

总结

在这里就不想上篇文章那样分各种情况去讨论了,那样有些无聊,这里就重要点的说明一下我的理解,最后附一个我写的demo供大家参考学习。

1、viewpager处理touch的思路:不截断touch,如果touch移动满足了一定的条件,再截断touch由该viewgroup处理。

2、我们可以看到onInterceptTouchEvent和onTouchEvent方法里的方法很相似,对于一个touch事件,这两个方法基本不会被都执行,只有很少的情况下这两个方法都会被执行。

3、viewpager有趣的现象,操作:单点操作viewpager,使viewpager响应事件,再加一个点触碰viewpager,如此不断加点,可以看到viewpager响应后加上去点的事件,这是为什么呢?代码B第108--111行已经告诉我们了,不再解释。

4、viewpager有趣的现象,操作:对于前面不不断加上去的触点,假设现在一共有4个触点,现在响应第4个触点的事件,如果第4个触点抬起,那么viewpager响应哪个触点的事件呢?答案是第一个。规律是什么呢?抬起的触点不是当前响应的,那么没影响;如果是当前响应的:如果抬起的触点的pointerIndex不是0,那么由pointerIndex最小的触点来响应,所以由第一个来响应。代码C已经很明显告诉我们了。如果这里你看不懂,那么你要学习一下android是怎么处理多触点事件的就会明白了。

Demo

下载地址:http://download.csdn.net/detail/u011647962/8507523






  相关解决方案