ViewPager源码解析之拖动和滑动

本文详细解析了Android中ViewPager的滑动和拖动机制,包括onInterceptTouchEvent()、performDrag()、pageScrolled()等关键方法的作用。通过分析,了解到ViewPager如何根据手势判断是否拦截触摸事件,以及在拖动和滑动过程中如何更新页面状态,实现平滑滚动和动画效果。
摘要由CSDN通过智能技术生成

概述

一个ViewGroup的滑动肯定和它的dispatchTouchEvent()、onInterceptTouchEvent()、onTouchEvent()有关。ViewPager重写了后两者,我们一个个来看。首先说明一下,ViewPager根据手势产生视图移动的方式有两种,一种是MOVE的时候随手指的拖动,一种是UP之后滑动到指定页面,而滑动是通过Scroller + computeScroll()实现的。

onInterceptTouchEvent()

Viewpager有一个mScrollState成员维护着ViewPager当前页面的状态,它可能被赋三个值。

/**
     * Indicates that the pager is in an idle, settled state. The current page
     * is fully in view and no animation is in progress.
     */
    //当前page空闲,没有动画
    public static final int SCROLL_STATE_IDLE = 0;

    /**
     * Indicates that the pager is currently being dragged by the user.
     */
//正在被拖动
    public static final int SCROLL_STATE_DRAGGING = 1;

    /**
     * Indicates that the pager is in the process of settling to a final position.
     */
    //正在向最终位置移动
    public static final int SCROLL_STATE_SETTLING = 2;

正如onInterceptTouchEvent()注释所说的,方法只是判断我们是否应该拦截这个Touch事件,scrolling都交给onTouchEvent()去做。因为在switch中每一个分支都有break,所以我调换了一下源码的顺序把DOWN放在了MOVE前面,这样更加清晰。

   @Override
    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.
      //如果一套手势结束,返回false
        if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
            // Release the drag.
            if (DEBUG) Log.v(TAG, "Intercept done!");
            resetTouch();
            return false;
        }

        // Nothing more to do here if we have decided whether or not we
        // are dragging.
        if (action != MotionEvent.ACTION_DOWN) {
          //如果正在被drag,拦截
            if (mIsBeingDragged) {
                if (DEBUG) Log.v(TAG, "Intercept returning true!");
                return true;
            }
          //不允许drag,不拦截
            if (mIsUnableToDrag) {
                if (DEBUG) Log.v(TAG, "Intercept returning false!");
                return false;
            }
        }

        switch (action) {
            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();
                //获取第一个触摸点的id
                mActivePointerId = ev.getPointerId(0);
                //设置允许拖拽为false
                mIsUnableToDrag = false;
                //标记为开始滚动
                mIsScrollStarted = true;
                //这个Scroller是在initViewPager()中创建的,这里手动调用计算一下                    //Scroller中的x,y值
                mScroller.computeScrollOffset();
              //如果此时正在向终态靠拢,并且离最终位置还有一定距离
                if (mScrollState == SCROLL_STATE_SETTLING
                        && Math.abs(mScroller.getFinalX() - mScroller.getCurrX()) > mCloseEnough) {
                    // Let the user 'catch' the pager as it animates.
                  //让用户抓住这个Pager

                  //停止滚动
                    mScroller.abortAnimation();
                  //???
                    mPopulatePending = false;
                  //更新缓存page信息(滚动的时候mCurItem会改变?)
                    populate();
                  //表示在拖动
                    mIsBeingDragged = true;
                  //不允许父ViewGroup拦截
                    requestParentDisallowInterceptTouchEvent(true);
                  //设置新的状态
                    setScrollState(SCROLL_STATE_DRAGGING);
                } else {
                  //这里我理解的是正在向终态靠近且距离足够小了,所以不能干涉移动
                    completeScroll(false);
                    mIsBeingDragged = false;
                }
                ....
                break;
            }
            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.
                 */
                //注释的很清楚,如果能进入这个地方说明mIsBeingDragged == false
                //这里要检查用户是否已经从原始位置移动的够远,以给mIsBeingDragged赋值


                //第一个触摸点的id
                final int activePointerId = mActivePointerId;
                if (activePointerId == INVALID_POINTER) {
                    // If we don't have a valid id, the touch down wasn't on content.
                    break;
                }
                //不太懂,貌似对第一个触摸点id做了一个转换
                final int pointerIndex = ev.findPointerIndex(activePointerId);
               //触摸点横坐标
                final float x = ev.getX(pointerIndex);
                //横向偏移
                final float dx = x - mLastMotionX;
                //横向偏移绝对值
                final float xDiff = Math.abs(dx);
                //纵向
                final float y = ev.getY(pointerIndex);
                final float yDiff = Math.abs(y - mInitialMotionY);
                ...

                  //这里是说如果在这个区域子View可以滑动,交给子View处理,不拦截
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值