无限滚动ViewPager的实现

Andorid 默认提供的 ViewPager 是不可以循环滚动的, 当滑动到边界时就不能滚动了. 但是实现 Banner 时, 体验最好的还是无限滚动, 为此需要自定义 ViewPager 来实现无限滚动.

PagerAdapter部分:

  1. 首先为了实现无限滚动,getCount()需要返回一个很大的数, 比如Integer.MAX_VALUE.

  2. 为了节省内存, 默认ViewPager只缓存前后1个Page, 就内存中只有perIndex, currentIndex, nextIndex 三个 View , 实际上也发现只需要三个 View 就可以实现无限滚动.

  3. instantiateItem() 方法中, 需要注意的就是获取到 position 对应的 View 后, 如果 view.getParent() 是 container 时, 需要先从 container 中移除该 View , 否则会导致 View 重复添加到 parent 报错.

     @Override
     public Object instantiateItem(ViewGroup container, int position) {
         //mView保存了3个View,用于实现轮播.
         //mView[position % 3]即获取该position对应的View
         View view = mViews[position % 3];
         if (container.equals(view.getParent())) {
             //防止添加到同一个parent
             container.removeView(view);
         }
         container.addView(view);
     }
  4. 重写 destroyItem() 方法, 什么都不操作, 不调用super方法. 这里之所以自己管理 View 的添加和移除, 是因为源码中是先调用 instantiateItem 添加再调用 destroyItem 移除的, 如果用它的方法, 3个 View 的时候重复添加 parent 就会 crash.

  5. isViewFromObject() 方法正常重写即可.

     @Override
     public boolean isViewFromObject(View view, Object object) {
         return view == object;
     }

ViewPager部分:

  1. 默认 ViewPager 的 Scroller 滑动时间是250ms, 轮播时滑动太快了,所以需要反射 Scroller 来设置合理的滑动时间:

     public class MyScroller extends Scroller {
         //默认1秒
         private int mDuration = 1000;
    
         public MyScroller(Context context) {
             this(context, null);
         }
    
         public MyScroller(Context context, Interpolator interpolator) {
             super(context, interpolator);
         }
    
         @Override
         public void startScroll(int startX, int startY, int dx, int dy, int duration) {
             // Ignore received duration, use fixed one instead
             super.startScroll(startX, startY, dx, dy, mDuration);
         }
    
         @Override
         public void startScroll(int startX, int startY, int dx, int dy) {
             // Ignore received duration, use fixed one instead
             super.startScroll(startX, startY, dx, dy, mDuration);
         }
     }
    
     //调用以下方法设置
     public void setViewPagerScrollTime() {
         try {
             Field mScroller = ViewPager.class.getDeclaredField("mScroller");
             mScroller.setAccessible(true);
             MyScroller scroller = new MyScroller(getContext());
             mScroller.set(this, scroller);
         } catch (Exception e) {
             e.printStackTrace();
         }
     }
  2. 注意 ViewPager 的 offset 设为1.

     `mViewPager.setOffscreenPageLimit(1);`
  3. 在设置 Adapter 或更新数据后, 记得设置 ViewPager 到中间页, 否则往左滑动就会到边界.

     //数据大小为size.
     int mid = Integer.MAX_VALUE / 2 - ((Integer.MAX_VALUE / 2) % size);
     holder.mViewPager.setCurrentItem(mid, false);