Android 使用ViewPager实现无限轮播出现空白bug原因及解决方案(Integer.MAX_VALUE实现方式)

本文深入探讨了无限轮播图在图片数量不足时出现的空白及异常问题,通过代码示例详细解释了ViewPager预加载机制导致的问题根源,并提供了解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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 就已经失败了,为啥没有报错?可能异常被捕获了,有兴趣的可以去看下源码。

以上说法如有错误,欢迎指出。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值