android开发:RecycleView和checkBox组合使用引起数据错乱解决方案

今天在使用RecycleViewcheckBox做列表时发现一个很有趣的问题,当我选中某一个checkBox后,RecycleView向下滑动时发现其他的checkBox也被选中了,bug图如下:

在这里插入图片描述

发生这个问题的原因在于RecycleView的复用机制,当我们向下滑动时RecycleView会复用离开屏幕的Holder从而来提高效率,而Holder会保存checkBox的选中状态,所以出现了上图这个bug。

解决方法:

使用一个集合来标记所有checkBox的位置和状态,点击checkBox时将位置和状态存入集合。当我们向下滑动时,新的item都会去集合中获取判断自己的选中状态,因为没有在集合存过所以返回false,因此新的item都是未选中的。而当我们向上滑动时,每个item也会去集合中获取判断自己的选中状态。如果之前我们选中过则会在集合中有记录,我们传入下标来获取item的状态。而SparseBooleanArray 作为这个集合则是最好的选择,它以intkeyboolean作为value,如果不存在则返回false


    public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
        List<Integer> mList;
        //保存状态的集合,SparseBooleanArray是以int为key,boolean作为value
        private SparseBooleanArray mCheckStates = new SparseBooleanArray();

        public MyAdapter(List<Integer> mList) {
            this.mList = mList;
        }

        @NonNull
        @Override
        public MyAdapter.MyViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
            MyViewHolder holder = new MyViewHolder(View.inflate(Main4Activity.this, R.layout.item_rv, null));
            return holder;
        }

        @Override
        public void onBindViewHolder(@NonNull MyViewHolder myViewHolder, final int i) {
            //以下标给每个checkBox加上唯一标识
            myViewHolder.checkBox.setTag(i);
            Log.e("tag", i+"");
            //通过标识去集合查找自己的状态,如果不存在则返回false
            myViewHolder.checkBox.setChecked(mCheckStates.get((Integer) myViewHolder.checkBox.getTag()));
            myViewHolder.checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
                @Override
                public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                    //checkBox被点击时将自己的标识(下标)和状态存入集合
                    mCheckStates.put((Integer) buttonView.getTag(), isChecked);
                }
            });
        }

        @Override
        public int getItemCount() {
            return mList.size();
        }

        class MyViewHolder extends RecyclerView.ViewHolder {
            CheckBox checkBox;
            public MyViewHolder(View view) {
                super(view);
                checkBox = (CheckBox) view.findViewById(R.id.checkBox);
            }
        }
    }

RecycleView加载数据时都会调用 onBindViewHolder()onBindViewHolder()中先是给每个item设置一个tag值,然后在访问集合获取状态,刚开时集合肯定没有item的记录因此则返回false,则调用setChecked()来设置状态。而我们对CheckBox进行监听,点击时将状态保存到集合。例如说我们点击了第一个CheckBox则集合中会保存它的下标和状态(true)。当我们向下滑动再向上滑动到第一项时,第一个item会根据自己的下标去集合获取状态,得到的是true则第一个item设置成选中的。

RecycleView和checkBox组合实现全选和取消全选功能:

弄懂了上面的问题之后这个功能就变得很简单了,我们只需要开放一个接口,外部通过这个接口去修改集合中所有的item的选中状态为true或者false,然后刷新RecycleView即可。


    public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
        List<Integer> mList;
        //保存状态的集合,SparseBooleanArray是以int为key,boolean作为value
        private SparseBooleanArray mCheckStates = new SparseBooleanArray();

        public MyAdapter(List<Integer> mList) {
            this.mList = mList;
        }

       /**
         * 设置全选的方法
         *
         * @param isSelect
         */
        public void setSelect(boolean isSelect) {
            for (int i = 0; i < mList.size(); i++) {
                //历遍整个数据源,将所有的item状态设成true或false
                mCheckStates.put(i, isSelect);
            }
        }

        @NonNull
        @Override
        public MyAdapter.MyViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
            MyViewHolder holder = new MyViewHolder(View.inflate(Main4Activity.this, R.layout.item_rv, null));
            return holder;
        }

        @Override
        public void onBindViewHolder(@NonNull MyViewHolder myViewHolder, final int i) {
            //给每个checkBox加上唯一标识
            myViewHolder.checkBox.setTag(i);
            Log.e("tag", i+"");
            //通过标识去集合查找自己的状态,如果不存在则返回false
            myViewHolder.checkBox.setChecked(mCheckStates.get((Integer) myViewHolder.checkBox.getTag()));
            myViewHolder.checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
                @Override
                public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                    //checkBox被点击时将自己的状态存入集合,tag作为key值
                    mCheckStates.put((Integer) buttonView.getTag(), isChecked);
                }
            });
        }

        @Override
        public int getItemCount() {
            return mList.size();
        }

        class MyViewHolder extends RecyclerView.ViewHolder {
            CheckBox checkBox;
            public MyViewHolder(View view) {
                super(view);
                checkBox = (CheckBox) view.findViewById(R.id.checkBox);
            }
        }
    }

调用:


        button1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
            	//调用接口
                adapter.setSelect(true);
                //刷新
                adapter.notifyDataSetChanged();
            }
        });
        button2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                adapter.setSelect(false);
                adapter.notifyDataSetChanged();
            }
        });

在这里插入图片描述

发布了194 篇原创文章 · 获赞 42 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/qq_39027256/article/details/103928164
今日推荐