美文网首页
viewpager解析笔记之onTouchEvent

viewpager解析笔记之onTouchEvent

作者: 将来是只猴 | 来源:发表于2018-04-11 16:43 被阅读0次

    前几天我本来打算从源码的角度分析一下viewpager的实现原理,后来证明我还是too young too simple!这个三千多行的代码我两天都没有看完,所以我打算一点一点的蚕食这个控件。今天我们就专门讲解这个类的onTouchEvennt()方法。首先还是源码贴上,但是都有了详细的注释,很容易明白:

    public boolean onTouchEvent(MotionEvent ev) {
        /**  如果当前view正在被拖拽,那么直接返回true,表明这个事件被消耗了 **/
        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;
        }
         /**  ev.getEdgeFlags()方法只有在 MotionEvent.ACTION_DOWN时设置,当触摸在边界时不消耗事件,返回false **/
        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;
         /**  action与MotionEvent.ACTION_MASK配合使用,可以获取多点触控事件 **/
        switch (action & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_DOWN: {
                mScroller.abortAnimation();
                mPopulatePending = false;
                /**  这个方法追踪进去会调用一个有参数的同名方法,方法的作用是移动到当前item,在这里就是一个定位作用 **/
                populate();
    
                // Remember where the motion event started
                mLastMotionX = mInitialMotionX = ev.getX();
                mLastMotionY = mInitialMotionY = ev.getY();
                mActivePointerId = ev.getPointerId(0);
                break;
            }
            case MotionEvent.ACTION_MOVE:
                if (!mIsBeingDragged) {
                    final int pointerIndex = ev.findPointerIndex(mActivePointerId);
                     /** mActivePointerId为我们按下时的点,如果返回-1说明这个点已经释放了,因此重置resettouche事件  **/
                    if (pointerIndex == -1) {
                        // A child has consumed some touch events and put us into an inconsistent
                        // state.
                        needsInvalidate = resetTouch();
                        break;
                    }
                    /** pointerIndex是移动的, 拿到当前点的横坐标 **/
                    final float x = ev.getX(pointerIndex);
                    final float xDiff = Math.abs(x - mLastMotionX);
                    final float y = ev.getY(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 = ev.findPointerIndex(mActivePointerId);
                    final float x = ev.getX(activePointerIndex);
                     /** 实现拖拽并重绘视图 **/
                    needsInvalidate |= performDrag(x);
                }
                break;
            case MotionEvent.ACTION_UP:
                if (mIsBeingDragged) {
                    final VelocityTracker velocityTracker = mVelocityTracker;
                    velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
                    int initialVelocity = (int) velocityTracker.getXVelocity(mActivePointerId);
                    mPopulatePending = true;
                    final int width = getClientWidth();
                    final int scrollX = getScrollX();
                    final ItemInfo ii = infoForCurrentScrollPosition();
                    final float marginOffset = (float) mPageMargin / width;
                    final int currentPage = ii.position;
                     /** 计算当前page的偏移量,用来确定需要滚动到当前页还是下一页 **/
                    final float pageOffset = (((float) scrollX / width) - ii.offset)
                            / (ii.widthFactor + marginOffset);
                    final int activePointerIndex = ev.findPointerIndex(mActivePointerId);
                    final float x = ev.getX(activePointerIndex);
                    final int totalDelta = (int) (x - mInitialMotionX);
                    /** 计算滑动到哪个页面 **/
                    int nextPage = determineTargetPage(currentPage, pageOffset, initialVelocity,
                            totalDelta);
                    setCurrentItemInternal(nextPage, true, true, initialVelocity);
    
                    needsInvalidate = resetTouch();
                }
                break;
            case MotionEvent.ACTION_CANCEL:
                if (mIsBeingDragged) {
                    scrollToItem(mCurItem, true, 0, false);
                    needsInvalidate = resetTouch();
                }
                break;
            case MotionEvent.ACTION_POINTER_DOWN: {//计算当前点和前一个DOWN点之间的距离,超过一定范围可以认为是多点模式
                final int index = ev.getActionIndex();
                final float x = ev.getX(index);
                mLastMotionX = x;
                mActivePointerId = ev.getPointerId(index);
                break;
            }
            case MotionEvent.ACTION_POINTER_UP://重置掉多点模式和记录的距离等等
                onSecondaryPointerUp(ev);
                mLastMotionX = ev.getX(ev.findPointerIndex(mActivePointerId));
                break;
        }
        if (needsInvalidate) {
            ViewCompat.postInvalidateOnAnimation(this);
        }
        return true;
    }
    

    关于多点触控用到的API:可方便上面代码阅读:
    event.getPointerCount():触控点的个数
    getPointerId(int pointerIndex):pointerIndex从0到getPointerCount-1,返回一个触摸点的标示
    getX(int pointerIndex):通过标示来得到X坐标
    getY(int pointerIndex):通过标示来得到Y坐标
    MotionEvent.ACTION_POINTER_1_DOWN:第一个触摸点点击事件
    MotionEvent.ACTION_POINTER_2_DOWN:第二个触摸点点击事件
    MotionEvent.ACTION_POINTER_1_UP:第一个触摸点松开事件
    MotionEvent.ACTION_POINTER_2_UP:第二个触摸点松开事件

    我对这个方法做了很详细的注释,相信应该不难看懂。通过解读,我们发现其实和我们自定义view时并没有什么太大区别,只是多了一些多点触控的判定以及操作。剩下的还是对viewpager的方法的操作。那么下一篇我们继续分析viewpager的其它方法,争取做到对viewpager的实现原理一清二楚。

    相关文章

      网友评论

          本文标题:viewpager解析笔记之onTouchEvent

          本文链接:https://www.haomeiwen.com/subject/xtqzhftx.html