ViewPager实现循环滑动

hthtps://blog.csdn.net/sxw1900/article/details/53668167


VviewPager实现循环滑动

以下一个非常常见的需求场景:
应用顶部的广告使用ViewPager实现自动滑动切换,但是另我们头疼的是ViewPager本身并没有可是实现循环滑动的设置项,看了几篇blog,实现都不是很理想,所以决定自己动手。为ViewPager添加滑动页主要依赖于PagerAdapter,里面有四个主角方法:

@Override
public Object instantiateItem(ViewGroup container, int position) {
  //页面显示和滑动过程中添加页面,比如滑动到第2页,会调用该方法提前加载第三页
}

@Override
public int getCount() {
  //获得总页数
}

@Override
public boolean isViewFromObject(View view, Object object) {
  //Google的建议写法:没有做详细探究
}

@Override
public void destroyItem(ViewGroup container, int position, Object object) {
  //页面显示和滑动过程中删除页面,比如滑动到第2页,会调用该方法将不在前后页(第2页的前后页为1、3页)的第0页移除
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

看完上面的介绍,相信已经有了思路,简单粗暴的方法就是将传入要显示的页面扩大到一个非常大的倍数,导致一直能够滑动下去,但这不是一个好方法。既然是页数决定能够滑动的次数,两个方法(instantiateItem()和destroyItem())决定滑动的过程中该添加哪一页和删去哪一页,那我们可以想到一个基本的思路——利用这三个方法加上除数取模的方式可以搞点事情:将getCount()的返回值放大到一个非常大的倍数,但是实际传入的List页数却并不放大,每次都通过用原本的List来取模的方式增加或删减页数。以下是实现方式:

public class SerialPagerAdapter extends PagerAdapter {

    private List<ImageView> imageViewList = new ArrayList<>();

    private int viewCount = 0;

    public SerialPagerAdapter(Context context, List<Bitmap> list) {
        viewCount = list.size();
        for (int i = 0; i < list.size(); i++) {
            ImageView imageView = new ImageView(context);
            imageView.setImageBitmap(list.get(i));
            imageViewList.add(imageView);
        }
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        container.addView(imageViewList.get(position % viewCount), 0);
        return imageViewList.get(position % viewCount);
    }

    @Override
    public int getCount() {
        return viewCount * 10000;
    }

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

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        container.removeView(imageViewList.get(position % viewCount));
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

我的一个项目Demo中随便引用了4个假的图片数据,“完美”运行,故以上的代码用了很长时间,但是当真的数据(3张宣传页)来临的时候,竟然神奇的发现崩溃了,产生了一个常见错误:

java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child’s parent first.

页面数量为3的滑动情况分析图

以上是当页面数量为3的时候出现崩溃原因的一张分析图:其中

  • 三角形表示当前处于哪一页
  • +号表示本次添加了哪个页面
  • -号表示本次移除了哪个页面
  • 显示的数字表示当前还存在的页面,画红线的表示当前操作已将该页面移除

通过打印log可以知道,每次切换到后一个页面,都是先删除不在紧邻旁边的页面添加下一页预览页面,而每次切换到前一个页面,过程相反,先add页面,再remove页面,这也就是以上异常崩溃原因(log请自行在两个方法中打印验证)。

分析图中,当从第二步执行到第三步的时候,先remove了页面0,再添加了页面3,但其实该页面在List中的下标为0,即list.size()%3 = 3%3 = 0,所以结果是2->3步骤,先remove页面0,再add页面0,但是往回滑动的时候,是先add页面,再remove页面,而3->2步骤为滑动到第1页,刚好要先add页面0,而上一步add的页面0还没有remove,导致了以上抛出的异常!为什么页数大于等于4的时候不会抛出异常,可以通过该方式分析。

既然原因找到了,那就直接说解决方案,很简单,还是放大List的倍数,当数量为1的时候,放到到4个条目,当为2的时候,也是放大4个条目,当数量为3的时候,很不巧,为了解决这个问题你要放大到6个条目,其实只要添加到4个就不会崩溃,但是选择放大倍数是不想图片滑动过程中顺序错乱,再说最大6个也是可以接受的!以下是解决之后的完整PagerAdapter代码,传入的List泛型为Bitmap,如果想传如其他可以自行修改,此处只是提供了一种解决思路。

完整Demo请访问Github链接,里面还有一个小福利(简单自定义的页面指示器,只是当前滑动到了哪一页的小圆点):

https://github.com/shixiuwen/SerialPagerAdapter

至于连续滑动,相信方法有很多,在这里使用RxJava的interval()方法实现,代码简洁:

vpBitmap.setAdapter(new SerialPagerAdapter(this, getBitmapList()));
pciIndicator.setPageCount(getBitmapList().size());  //设置下标点的个数
//设置自动切换
Observable.interval(2, TimeUnit.SECONDS)
          .subscribeOn(Schedulers.newThread())
          .observeOn(AndroidSchedulers.mainThread())
          .subscribe(new Action1<Long>() {
               @Override
               public void call(Long aLong) {
                  vpBitmap.setCurrentItem(vpBitmap.getCurrentItem() + 1, true);
                    }
                });
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

以下是PagerAdapter完整代码:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值