Android ViewPager使用及爬坑

今天在学习一个常用的ViewPager控件,遇到了一些小坑,在这里记录下来。

根本原因在于ViewPager的缓存机制。因为缓存,出现了一个状况:左滑时会闪退并且报错

 java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.。

其根本原因在于viewPager的缓存机制,只在三张图片的时候会出现这个问题,当把轮播图增加到四张时,问题就解决了😂😂。

先按照我的学习过程走一遍,首先是viewpager的使用。

 <androidx.viewpager.widget.ViewPager
      android:id="@+id/picture_viewPager"
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      android:layout_centerInParent="true"/>

ViewPager的定义比较简单,唯一需要注意的是在新版本的AndroidStudio中ViewPager已经不再从V4包导入,而是改为从androidx包里面导入。

然后是在MainActivity中定义一下。


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        picture_text = findViewById(R.id.picture_text);
        picture_viewPager = findViewById(R.id.picture_viewPager);
        initData();
        initView();
    }

前置工作完成。下一步是初始化数据:

private void initData(){
        image_ids = new int[]  {R.drawable.pager_image1,R.drawable.pager_image2,R.drawable.pager_image3};
      
        //添加图片到图片列表里
        imageViewList = new ArrayList<>();
        ImageView iv;
        for (int i = 0; i < image_ids.length; i++) {
            iv = new ImageView(this);
            iv.setBackgroundResource(image_ids[i]);//设置图片
            iv.setId(image_ids[i]);//顺便给图片设置id   
            imageViewList.add(iv);
        }
    }

 private void initView(){
        ViewPagerAdapter viewPagerAdapter = new ViewPagerAdapter(imageViewList, picture_viewPager);
        picture_viewPager.setAdapter(viewPagerAdapter);
   }

图片啥的,就在本地选了几个图片作为本地资源了。后面就设置适配器adapter了。

接下来新建一个ViewPagerAdapter继承自PagerAdapter,这个比较简单,直接copy

/**
 * com.fenger.viewpager
 * Created by fenger
 * in 2020-01-06
 */
public class ViewPagerAdapter extends PagerAdapter {

    private List<ImageView> imageViewList;
    private ViewPager viewPager;

    /**
     * 构造方法,传入图片列表和ViewPager实例
     * @param images
     * @param viewPager
     */

    public ViewPagerAdapter(List<ImageView> images, ViewPager viewPager){
        this.imageViewList  = images;
        this.viewPager = viewPager;
    }


    @Override
    public int getCount() {
//        return imageViewList.size() * 2;
        return Integer.MAX_VALUE;//保证能够无限循环
    }

    // 4.指定复用的判断逻辑(一般为固定写法)
    @Override
    public boolean isViewFromObject(View view, Object object) {
        boolean a = view == object;
        return a;
    }

    // 2.返回要显示的条目,并创建条目
    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        //container:容器,其实也就是ViewPager
        //position:当前要显示的条目的位置

        int newPosition = position % imageViewList.size();
        ImageView imageView = imageViewList.get(newPosition);
        Log.e("fenger", "创建条目:"+position );
        //a.将View对象添加到container容器中
        container.addView(imageView);

        //b.把View对象返回给框架,适配器
        return imageView;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {

        container.removeView((View) object);
        Log.e("fenger","删除条目:"+position);
    }
}

这里注意下我在删除条目和创建条目两个地方打印了日志文件,这也是后面报bug的原因处。

viewPager的小知识点就这些,接下来软件跑起来。

基本的循环效果确实出来了,那么滑动一下。同时利用日志一步步调试。

1.应用启动加载viewPager的时候

打印了创建0和1。0是一开始的初始图片,1是预加载的一张图片。在这里我就以为viewPager是回一次性缓存一张,加载一张,一共两张图片。

2.在第一次向右滑的时候

创建了第2张图片,这是因为现在显示的是第1张图片,所以缓存第2张。

3.第一次向左滑

删除了第2张,也就是说只缓存下一张图片。

4,再次向右滑,第三次向右滑

这一次和第二次的结果一样,只是创建了第2张和第3张,删除了第0张。

5,向左滑动

只要到了2以后的所有图片,全部都只要左滑就闪退。

闪退的代码是

//a.将View对象添加到container容器中
container.addView(imageView);

具体原因也大概有解释:这个imageView有个父控件占有了它。

这里可以说明一下,首先一开始我以为只是加载的后一张缓存,但是经过之后的操作会发现,前一张不会被删除,但是回删除更前面的图片的缓存,所以应该是加载中间一张图片,同时缓存前后两张图片。然后就是,假设现在有3张图片(4,5,6,显示的是5,缓存的是4和6),由于

在这里是直接把imageView加载到container里面,但是我已经缓存了三张图片。在向右滑(显示的变为6)的时候,根据日志可以看出,是先删除后创建,所以4里面的图片被拿了出来,创建一个7,并且把图片放到了7里面。从6向左滑的时候,先创建后删除,所以先创建了4,结果发现要用的imageView正在7里面,所以就会出现报错。因此,在图片数量大于等于四的时候,这里应该不会出现这个问题。经过测试成功了。

但是万一必须要用三张轮播图呢?有几个方案:1是舍弃轮播图缓存机制,2是重写ViewPager,在左滑的时候先删除后创建(不确定会不会出问题,没看过源码),3把三张循环一次变成六张,伪装成三张使用(视觉效果相同,我觉得不错),4.不直接addView的时候添加View,而是新建一个view进行添加(这也是我发现原因的起因,同一个view不能被两个地方调用?这个问题之后研究下)

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值