给 ViewPager 添加一个自定义的滚动监听器

2,187 阅读4分钟

ViewPager是在日常开发业务中使用较多的,通常我们都会使用OnPagerChangeListener来监听ViewPager页面的滚动和状态变化:

mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
    @Override
    public void onPageScrolled(int position, float positionOffset, int     positionOffsetPixels) {
        // 页面正在滚动时不断调用
        Log.d("ViewPager", "onPageScrolled————>"        
              + "    position:" + position        
              + "    positionOffest:" + positionOffset       
              + "    positionOffsetPixels:" + positionOffsetPixels);
    }

    @Override
    public void onPageSelected(int position) {
        // 页面选中时调用
        Log.d("ViewPager", "onPagerSelected————>    position:" +position);
    }

    @Override
    public void onPageScrollStateChanged(int state) {
        // 页面的滚动状态变化时调用
        Log.d("ViewPager", "onPageScrollStateChanged————>    state:" + state);
    }
});

在onPageScrolled方法中:
position:在滑动过程中,有可能是你当前页面位置,也有可能是你要滑动到的下一个页面位置;
positionOffset:页面位置的偏移量,值为0到1之间。页面从右向左滑出时,值从0逐渐变为1并且在趋近1的时候突变为0。页面从左向右滑出时,值从1逐渐变为0;
positionOffsetPixels:页面滑动的像素值,从右向左滑动时,该值逐渐变大,从左向右滑动时,逐渐变小。

从第0页滑动到第1页onPageScrolled调用:


onPageScrolled0-1.png


从第1页滑动到第2页onPageScrolled调用:


onPageScrolled1-2.png


从第2页滑动回到第1页onPageScrolled调用:


onPageScrolled2-1.png


从第1页滑动回到第0页onPageScrolled调用:


onPageScrolled1-0.png


通常情况下,这个监听器中的三个回调方法已经完全能够满足我们的业务需要。
但是因为第一个参数position在滑动过程中不是很明确的表示是当前页面位置还是下一个页面的位置,我们还需要通过positionOffset或者positionOffsetPixels来判断。
因此我们通过OnPageChangedListener来封装一个自定义的滚动监听器,使用这个自定义的滚动监听器,在页面滑动时,可以很明确的回调进入的页面,离开的页面,以及滑动百分比、状态和最后选中的页面。

1.定义滚动监听接口

/**
 * ViewPage的页面滚动监听器
 */
public interface OnPageScrollListener {
    /**
     * 页面滚动时调用
     *
     * @param enterPosition 进入页面的位置
     * @param leavePosition 离开的页面的位置
     * @param percent       滑动百分比
     */
    void onPageScroll(int enterPosition, int leavePosition, float percent);

    /**
     * 页面选中时调用
     *
     * @param position 选中页面的位置
     */
    void onPageSelected(int position);

    /**
     * 页面滚动状态变化时调用
     *
     * @param state 页面的滚动状态
     */
    void onPageScrollStateChanged(int state);
}

2.在ViewPagerHelper中来实现滑动时页面位置、百分比的回调

/**
 * ViewPager辅助类
 */
public class ViewPagerHelper {

    private double mLastPositionOffsetSum;  // 上一次滑动总的偏移量
    private OnPageScrollListener mOnPageScrollListener;

    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        // 当前总的偏移量
        float currentPositionOffsetSum = position + positionOffset;
        // 上次滑动的总偏移量大于此次滑动的总偏移量,页面从右向左进入(手指从右向左滑动)
        boolean rightToLeft = mLastPositionOffsetSum <= currentPositionOffsetSum;
        if (currentPositionOffsetSum == mLastPositionOffsetSum) return;
        int enterPosition;
        int leavePosition;
        float percent;
        if (rightToLeft) {  // 从右向左滑
            enterPosition = (positionOffset == 0.0f) ? position : position + 1;
            leavePosition = enterPosition - 1;
            percent = (positionOffset == 0.0f) ? 1.0f : positionOffset;
        } else {            // 从左向右滑
            enterPosition = position;
            leavePosition = position + 1;
            percent = 1 - positionOffset;
        }
        if (mOnPageScrollListener != null) {
            mOnPageScrollListener.onPageScroll(enterPosition, leavePosition, percent);
        }
        mLastPositionOffsetSum = currentPositionOffsetSum;
    }

    public void onPageSelected(int position) {
        if (mOnPageScrollListener != null) {
            mOnPageScrollListener.onPageSelected(position);
        }
    }

    /**
     * @param state 当前滑动状态
     *              ViewPager.SCROLL_STATE_IDLE     页面处于闲置、稳定状态,即没被拖动也没惯性滑动
     *              ViewPager.SCROLL_STATE_DRAGGING 页面正在被用户拖动,即手指正在拖动状态
     *              Viewpager.SCROLL_STATE_SETTLING 页面处于即将到达最终状态的过程,即手指松开后惯性滑动状态
     */
    public void onPageScrollStateChanged(int state) {
        if (mOnPageScrollListener != null) {
            mOnPageScrollListener.onPageScrollStateChanged(state);
        }
    }

    public void setOnPageScrollListener(OnPageScrollListener onPageScrollListener) {
        mOnPageScrollListener = onPageScrollListener;
    }
}

3.采用工具类ViewPagerUtil来绑定ViewPager和OnPageScrollListener

/**
 * ViewPager工具类
 */
public class ViewPagerUtil {
    /**
     * 给ViewPager绑定自定义的滚动监听
     *
     * @param viewPager
     * @param onPageScrollListener
     */
    public static void bind(@NonNull ViewPager viewPager, @NonNull OnPageScrollListener onPageScrollListener) {
        final ViewPagerHelper helper = new ViewPagerHelper();
        // 给helper设置滚动监听
        helper.setOnPageScrollListener(onPageScrollListener);
        viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
                helper.onPageScrolled(position, positionOffset, positionOffsetPixels);
            }

            @Override
            public void onPageSelected(int position) {
                helper.onPageSelected(position);
            }

            @Override
            public void onPageScrollStateChanged(int state) {
                helper.onPageScrollStateChanged(state);
            }
        });
    }
}

4.调用工具类方法实现绑定

ViewPagerUtil.bind(mViewPager, new OnPageScrollListener() {
    @Override
    public void onPageScroll(int enterPosition, int leavePosition, float percent) {
        Log.d("ViewPager", "onPageScrolled————>"
              + "    进入页面:" + enterPosition
              + "    离开页面:" + leavePosition
              + "    滑动百分比:" + percent);
    }

    @Override
    public void onPageSelected(int position) {
        Log.d("ViewPager", "onPageSelected————>    position:" + position);
    }

    @Override
    public void onPageScrollStateChanged(int state) {
        Log.d("ViewPager", "onPageScrollStateChanged————>    state:" + state);
    }
});

运行结果:
从第0页滑动到第1页onPageScroll调用:


onPageScrolled0-1.png


从第1页滑动到第2页onPageScroll调用:


onPageScrolled1-2.png


从第2页滑动到第1页onPageScroll调用:


onPageScrolled2-1.png


从第1页滑动到第0页onPageScroll调用:


onPageScrolled1-0.png

通过封装过后的OnPageScrollListener,可以很方便的定义各种样式的ViewPager指示器。