ViewPager实现循环滑动
来源:互联网 发布:截图软件百度云 编辑:程序博客网 时间:2024/05/16 05:00
ViewPager实现循环滑动
以下一个非常常见的需求场景:
应用顶部的广告使用ViewPager实现自动滑动切换,但是另我们头疼的是ViewPager本身并没有可是实现循环滑动的设置项,看了几篇blog,实现都不是很理想,所以决定自己动手。为ViewPager添加滑动页主要依赖于PagerAdapter,里面有四个主角方法:
@Overridepublic Object instantiateItem(ViewGroup container, int position) { //页面显示和滑动过程中添加页面,比如滑动到第2页,会调用该方法提前加载第三页}@Overridepublic int getCount() { //获得总页数}@Overridepublic boolean isViewFromObject(View view, Object object) { //Google的建议写法:没有做详细探究}@Overridepublic void destroyItem(ViewGroup container, int position, Object object) { //页面显示和滑动过程中删除页面,比如滑动到第2页,会调用该方法将不在前后页(第2页的前后页为1、3页)的第0页移除}
看完上面的介绍,相信已经有了思路,简单粗暴的方法就是将传入要显示的页面扩大到一个非常大的倍数,导致一直能够滑动下去,但这不是一个好方法。既然是页数决定能够滑动的次数,两个方法(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)); }}
我的一个项目Demo中随便引用了4个假的图片数据,“完美”运行,故以上的代码用了很长时间,但是当真的数据(3张宣传页)来临的时候,竟然神奇的发现崩溃了,产生了一个常见错误:
java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child’s parent first.
以上是当页面数量为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); } });
以下是PagerAdapter完整代码:
/** * Created by ShiXiuwen on 2016/12/08. * <p> * Description: 对应主界面自动滑动的ViewPager * <p> * 为了实现连续滑动,将数组count放大10000倍,实际上未放大10000View的 * 个数,只是用了除数取余的方法由于ViewPager会缓存前后两个View,所以导 * 致在个数小于3的时候会出现重复添加的情况,故如果内容页面小于等于三,将 * 数组放大到大于等于三即可(4,6),如果内容页大于3,则可直接使用 */public class SerialPagerAdapter extends PagerAdapter { private List<ImageView> imageViewList = new ArrayList<>(); private int oldViewCount = 0; private int newViewCount; //如果内容页小于等于三,放大倍数到大于3 public SerialPagerAdapter(Context context, List<Bitmap> list) { oldViewCount = list.size(); List<Bitmap> newList = plusImageViewList(list);//将图片数量增加到>=4 newViewCount = newList.size(); for (int i = 0; i < newList.size(); i++) { ImageView imageView = new ImageView(context); imageView.setImageBitmap(newList.get(i)); imageViewList.add(imageView); } } /** * 扩大图片列表数量到 >=4 的状态 */ private List<Bitmap> plusImageViewList(List<Bitmap> list) { if (list.size() == 1) { //添加到4个数据 for (int i = 0; i < 3; i++) { list.add(list.get(0)); } } if (list.size() == 2) { //添加到4个数据 list.add(list.get(0)); list.add(list.get(1)); } if(list.size() == 3){ //添加到6个数据 list.add(list.get(0)); list.add(list.get(1)); list.add(list.get(2)); } return list; } @Override public Object instantiateItem(ViewGroup container, int position) { Log.e("amos->add", "position:" + position + " " + "position % newViewCount:" + position % newViewCount); container.addView(imageViewList.get(position % newViewCount), 0); return imageViewList.get(position % newViewCount); } @Override public int getCount() { return oldViewCount * 10000; } @Override public boolean isViewFromObject(View view, Object object) { return view == object; } @Override public void destroyItem(ViewGroup container, int position, Object object) { Log.e("amos->remove", "position:" + position + " " + "position % newViewCount:" + position % newViewCount); container.removeView(imageViewList.get(position % newViewCount)); }}
- ViewPager实现循环滑动
- ViewPager实现循环滑动
- ViewPager实现左右循环滑动
- ViewPager实现真循环滑动
- 使用ViewPager实现左右循环滑动
- 【Android开发】ViewPager实现左右循环滑动
- 使用ViewPager实现左右循环滑动图片
- 使用ViewPager实现左右循环滑动 总结
- Android ViewPager 实现无限循环滑动
- ViewPager实现左右无限循环滑动
- ViewPager实现循环滑动功能说明
- Viewpager完美实现左右循环滑动
- 使用ViewPager实现左右循环滑动
- Android Fragment+ViewPager实现循环滑动
- 使用ViewPager实现左右循环滑动
- 使用ViewPager实现左右循环滑动图片
- Android ViewPager实现无限循环滑动
- 实现ViewPager的左右循环滑动
- php 序列号和反序列化
- Android ListView工作原理完全解析,带你从源码的角度彻底理解
- 微信小程序之网络请求
- 自定义AlertDialog中的EditText无法弹出软键盘的解决
- Web请求模型
- ViewPager实现循环滑动
- The original file 'AndroidManifest.xml' has been deleted or is not accessible
- ios和h5交互
- bindService报空指针错误故障排除
- AccessibilityNodeInfo自动输入
- 怎么样种植4棵树木,使其中任意两棵树的距离相等?
- 第一天---看代码
- angular2.0指令 (directive) 和组件 (component) 的生命周期解析
- sphinx –rotate机制详解