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

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

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

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
使用ViewPager实现轮播图的步骤如下: 1. 在布局文件中添加ViewPager控件和指示器(可选)。 ```xml <androidx.viewpager.widget.ViewPager android:id="@+id/view_pager" android:layout_width="match_parent" android:layout_height="200dp" /> <LinearLayout android:id="@+id/indicator_layout" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:orientation="horizontal" /> ``` 2. 创建一个PagerAdapter,设置数据源和页面布局。 ```java public class MyPagerAdapter extends PagerAdapter { private List<Integer> mData; private LayoutInflater mInflater; public MyPagerAdapter(Context context, List<Integer> data) { mData = data; mInflater = LayoutInflater.from(context); } @Override public int getCount() { return mData.size(); } @Override public boolean isViewFromObject(@NonNull View view, @NonNull Object object) { return view == object; } @NonNull @Override public Object instantiateItem(@NonNull ViewGroup container, int position) { View view = mInflater.inflate(R.layout.item_pager, container, false); ImageView imageView = view.findViewById(R.id.image_view); imageView.setImageResource(mData.get(position)); container.addView(view); return view; } @Override public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) { container.removeView((View) object); } } ``` 3. 绑定PagerAdapter和ViewPager,并设置ViewPager的滑动监听,以便更新指示器。 ```java ViewPager viewPager = findViewById(R.id.view_pager); MyPagerAdapter adapter = new MyPagerAdapter(this, mData); viewPager.setAdapter(adapter); LinearLayout indicatorLayout = findViewById(R.id.indicator_layout); for (int i = 0; i < mData.size(); i++) { View indicator = new View(this); int size = getResources().getDimensionPixelSize(R.dimen.indicator_size); LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(size, size); lp.leftMargin = i == 0 ? 0 : getResources().getDimensionPixelSize(R.dimen.indicator_margin); indicator.setLayoutParams(lp); indicator.setBackgroundResource(R.drawable.indicator_bg); indicatorLayout.addView(indicator); } viewPager.addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() { @Override public void onPageSelected(int position) { super.onPageSelected(position); for (int i = 0; i < mData.size(); i++) { View indicator = indicatorLayout.getChildAt(i); indicator.setSelected(i == position); } } }); ``` 其中,item_pager布局文件中只包含一个ImageView控件,用于显示图片。indicator_bg是指示器的背景,可以自定义。 以上就是使用ViewPager实现轮播图的主要步骤。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值