1、实现无限轮播的方案
方案二:就是在pagerAdapter 的 getCount方法中,return Integer.MAX_VALUE,
但在使用改方法,如果轮播图数量少于3张时,滑动就会出现
java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.
2、报错原因
在说明之前先粘贴代码方便查看:
public class BannerActivity extends AppCompatActivity {
private ViewPager mViewPager;
private int[] res = new int[]{R.mipmap.ic_image_1, R.mipmap.ic_image_2};
// private int[] res = new int[]{R.mipmap.ic_image_1, R.mipmap.ic_image_2,R.mipmap.ic_image_3,R.mipmap.ic_image_4};
private List<View> mBanner;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_banner);
mViewPager = findViewById(R.id.view_pager);
mBanner = new ArrayList<>();
for (int i = 0; i < res.length; i++) {
ImageView imageView = new ImageView(this);
imageView.setImageResource(res[i]);
mBanner.add(imageView);
}
InnerViewPagerAdapter adapter = new InnerViewPagerAdapter();
mViewPager.setAdapter(adapter);
mViewPager.setCurrentItem(Integer.MAX_VALUE / 2-mBanner.size()/2);
}
class InnerViewPagerAdapter extends PagerAdapter {
@Override
public int getCount() {
return Integer.MAX_VALUE;
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
Log.e(this.getClass().getName(),"instantiateItem。position="+position);
position = position % mBanner.size();
container.addView(mBanner.get(position));
Log.e(this.getClass().getName(),"instantiateItem.view_size="+container.getChildCount());
return mBanner.get(position);
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
Log.e(this.getClass().getName(),"destroyItem。position="+position);
position = position % mBanner.size();
container.removeView(mBanner.get(position));
Log.e(this.getClass().getName(),"destroyItem.view_size="+container.getChildCount());
}
}
}
上面代码可以看到,图片资源只有2张,adapter的getCount方法return Integer.MAX_VALUE,默认位置设置
mViewPager.setCurrentItem(Integer.MAX_VALUE / 2-mBanner.size()/2);
即在总长度的中间部分的第一张。
运行效果:正常打开,右滑有图(崩溃),左滑空白(崩溃)。
问题:1、左滑为啥会空白。
在代码中我做了log打印
可以看到adapter中的 instantiateItem 方法运行了三次,这是因为ViewPager的缓存策略,会默认缓存当前显示位置的上一张和下一张,可以看下 instantiateItem。position数字的打印顺序,1073741822–>1073741821–>1073741823,可以看出加载顺序是 “当前”—>“左边”—>“右边”。
在代码中,只创建了2个ImageView,分别显示2张图片。所以对应逻辑如下图:
因为ViewPager预加载的顺序是先左边再右边,第1073741821个item已经把 ImageView2加载进ViewGroup中了, 当第1073741823个item想要加载ImageView2时,因为ImageView2已有父控件,所以无法再进行添加,这就是左滑会出现空白的原因。
问题2:为啥设置3张图片以下会报错
下面先看看滑动后打印的log
先看右滑的log,看到adapter会调用instantiateItem方法加载第1073741820个item,而相对于的图片是ImageView1,而这是ImageView1还在ViewGroup中没有被移除,便抛出错误异常。
左滑的原理也是一样,看log知道先把第1073741821个item移除(ImageView2),然后加载第1073741824个item(ImageView1),而ImageView1还在ViewGroup中没有被移除,所以报异常
解决方案
@Override
public Object instantiateItem(ViewGroup container, int position) {
View v = list.get(position % list.size());
ViewGroup parent = (ViewGroup) v.getParent();
if (parent != null) {
parent.removeView(v);
}
container.addView(v);
return v;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
}
在加载前判断是否有父控件持有着,有的话先移除。
猜想:在刚打开的时候,右边的item addView 就已经失败了,为啥没有报错?可能异常被捕获了,有兴趣的可以去看下源码。
以上说法如有错误,欢迎指出。