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();
            }
        });

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章