-
viewpager的简单使用
如果使用viewpager来实现fragment的左右滑动的话,那么直接继承FragmentStatePagerAdapter或FragmentPagerAdapter重写其方法即可
(FragmentStatePagerAdapter:在不需要的时候会销毁fragment实例,一般用于大量数据zaifragment上显示的场景;FragmentPagerAdapter:在不需要的时候只会销毁fragment的视图,不会销毁fragment的实例,一般用于只有少量fragment但需要频繁切换的场景)
但如果想实现非fragment的滑动则需要重写原生的pagerAdapter,并且实现其中的四个方法:
方法
作用
getCount()
adapter中数据的数量
isViewFromObject(@NonNull View view, @NonNull Object object)
用来判断instantiateItem函数返回的值和当前view是否对应
*viewpager不直接处理每一个视图而是将各个视图与一个键联系起来。这个键用来跟踪且唯一代表一个页面(当然这个方法中可以直接返回视图本身,用视图本身做键,并且在instantiateItem方法里返回该page本身)
instantiateItem(@NonNull ViewGroup container, int position)
为给定的位置创建pager视图
destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object)
删除指定位置中的pager视图
之后在viewpager实例中setAdapter()即可实现一个简单的viewpager。
加载当前页面时会把前后页面都加载好,作为过度页面,并且遍历其他页面将其删除。可以通过setOffscreenPageLimit(int)方法定制预加载页面的数量.
-
viewpager实现无限轮播
实现轮播其实很简单只要用handler的延迟发送消息即可
private final Runnable mLoopRunnable = new Runnable() { @Override public void run() { currentPosition1 = viewPager1.getCurrentItem(); currentPosition1++; //设置下一个page是当前 viewPager1.setCurrentItem(currentPosition1, false); //此处适当加一下下一页面的处理逻辑 。。。 //循环发消息 mHandler.postDelayed(this, mDelayedTime); } };
然而要实现无限轮播,网上普遍的有两种方式
-
第一种是将pagerAdapter大小设置成Integer.MAX_VALUE并将起始位置设置成Integer.MAX_VALUE的中间
//当前选中页 private int currentPosition; //数据项个数 private List<Integer> itemList; public static final int mLooperCount = 500; //设置当前选中的item currentPosition = getStartItem(); viewPager1.setCurrentItem(currentPosition1); private int getStartItem() { if(getRealCount() == 0){ return 0; } // 我们设置当前选中的位置为Integer.MAX_VALUE / 2,这样开始就能往左滑动 // 但是要保证这个值与getRealPosition 的 余数为0,因为要从第一页开始显示 int currentItem =BannerAdapter.mLooperCount / 2; if(currentItem % getRealCount() ==0 ){ return currentItem; } // 直到找到从0开始的位置 while (currentItem % getRealCount() != 0){ currentItem++; } return currentItem; } //获取数据项个数 private int getRealCount() { return itemList == null ? 0 : itemList.size(); } public int getCount() { return Integer.MAX_VALUE; } 代码抄自网络,但反正实现大致就是这个样子
-
第二种是在页面前后分别添加最后一页和第一页的数据,并且当滑到0和最后一页的时候默默的将页面切到倒数第二页和第一页
具体实现可参考:ViewPager两种方式实现无限轮播 - 掘金
推荐第二种,第一种可能会出现白屏甚至anr。
3. viewPager数据更新时无限轮播的坑
数据更新有两种方法,但都需要进行相应的封装:
-
每次有新数据来都重新setAdapter()
正常使用这个方法其实是没有问题的,但是当想实现无限轮播效果的时候,setAdapter会出现白屏闪烁的现象,甚至会出现anr。
白屏和anr都是因为轮播的技术选型选择了无限大的那个方法才会出现的,但是无论选择那个方法数据刷新都会有闪烁。
怀疑是populate()方法绘制图片的问题
若要避免白屏和anr可参考此链接进行修改ViewPager anr,页面空白问题完全解析 - 简书(但其实并不推荐此方法)
-
有数据来之后,更新PagerAdapter中的数据,并且调用adapter的notifyDataSetChanged()方法
notifyDataSetChanged()方法最终调用dataSetChanged()方法,而在这个方法中,判断item是否替换掉是根据一个position标签位进行判断的,而获得这个标签位的方法在mAdapter.getItemPosition(ii.object)中
然而这个getItemPosition()方法是固定返回POSITION_UNCHANGED也就是-1,也就是说靠原先的方法永远也不会更新数据。那么我们的做法就是重写这个getItemPosition方法,使其在合适的时机返回合适的值。
此处是事先在view中设置tag,将位置与判断条件都存起来,在 getItemPosition()方法中将其取出作为判断两个view的内容是否一致的条件,返回对应的标签位。
存tag的时机应该放在初始化view的地方,即instantiateItem()中。
@Override public Object instantiateItem(ViewGroup container, int position) { 。。。 View view = initView(pos); view.setTag(bannerItems.get(pos).getImageUrl()); view.setTag(R.string.tag_position_key, pos); 。。。 container.addView(view, params); return view; }
@Override public int getItemPosition(Object object) { if (object instanceof View){ View view = (View) object; String imageUrl = (String) view.getTag(); int position = (int) view.getTag(R.string.tag_position_key); int b = imageUrl.equals(bannerItems.get(position).getImageUrl()) ? POSITION_UNCHANGED : POSITION_NONE; return b; } return super.getItemPosition(object); }