RecyclerView中使用CheckBox出现勾选混乱的解决方案

来源:互联网 发布:中国移动软件有哪些 编辑:程序博客网 时间:2024/05/19 03:22

熟悉RecyclerView的人应该都知道,RecyclerView使用了复用机制,当在RecyclerView中得每一项都添加一个CheckBox时,勾选当前页面的几个CheckBox会发现下面还有其他的CheckBox也被勾选了,今天我们就来讨论一下如何解决这个问题。


首先当然是创建一个项目,然后在activity_main中添加一个RecyclerView控件,当然,在这之前,我们需要先添加RecyclerView的依赖,如下图:





然后 开始编辑activity_main:

<?xml version="1.0" encoding="utf-8"?><LinearLayout     xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="vertical">        <android.support.v7.widget.RecyclerView        android:id="@+id/id_recycler_view"        android:layout_width="match_parent"        android:layout_height="match_parent">            </android.support.v7.widget.RecyclerView></LinearLayout>



接下来为这个RecyclerView创建一个item布局文件,命名为item_recyclerview,并添加一个CheckBox空间,代码如下:

<?xml version="1.0" encoding="utf-8"?><LinearLayout     xmlns:android="http://schemas.android.com/apk/res/android"    android:orientation="vertical"     android:layout_width="match_parent"    android:layout_height="60dp"    android:gravity="center_vertical">    <CheckBox        android:id="@+id/id_check_box"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:layout_marginLeft="10dp"        android:text="CheckBox"/></LinearLayout>



接下来要编辑MainActivity了,包括从布局文件中找到刚才的RecyclerView控件,然后为其设置Adapter等,过程不再详细叙述,编辑后的代码如下:

public class MainActivity extends AppCompatActivity {    private RecyclerView recyclerView;    private MyAdapter myAdapter;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        recyclerView = (RecyclerView) findViewById(R.id.id_recycler_view);        myAdapter = new MyAdapter();        recyclerView.setLayoutManager(new LinearLayoutManager(this));        recyclerView.setAdapter(myAdapter);    }    private class MyAdapter extends RecyclerView.Adapter {        private List<String> content;        @Override        public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {            View view = LayoutInflater.from(MainActivity.this).inflate(R.layout.item_recyclerview, parent, false);            return new MyViewHolder(view);        }        @Override        public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {            MyViewHolder myViewHolder = (MyViewHolder) holder;            myViewHolder.checkBox.setText(content.get(position));        }        @Override        public int getItemCount() {            content = new ArrayList<>();            for (int i = 0; i < 100; i++) {                content.add("CheckBox" + i);            }            return content.size();        }    }    private class MyViewHolder extends RecyclerView.ViewHolder {        private CheckBox checkBox;        public MyViewHolder(View itemView) {            super(itemView);            checkBox = (CheckBox) itemView.findViewById(R.id.id_check_box);        }    }}


可以看到,我们为这个RecyclerView设置了100个item,每个item里面都含有一个CheckBox,这时候运行这个应用,勾选出现的屏幕上的某一个或者多个CheckBox之后,当你向下拉的时候,问题出现了,你会发现下面会有很多的CheckBox也被选中了。下面我们就来着手解决这个问题,其实要解决也很简单,可以定义一个boolean类型的数组或者列表,用它来控制CheckBox的选中状态,当某个CheckBox被选中的时候将其选中状态记录在数组或列表中,当某个CheckBox滚动到屏幕上的时候,再用数组或列表中对应的值把它的选中状态改回来就好了,修改后的代码如下:

public class MainActivity extends AppCompatActivity {    private RecyclerView recyclerView;    private MyAdapter myAdapter;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        recyclerView = (RecyclerView) findViewById(R.id.id_recycler_view);        myAdapter = new MyAdapter();        recyclerView.setLayoutManager(new LinearLayoutManager(this));        recyclerView.setAdapter(myAdapter);    }    private class MyAdapter extends RecyclerView.Adapter {        private List<String> content;        private boolean[] flag = new boolean[100];//此处添加一个boolean类型的数组        @Override        public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {            View view = LayoutInflater.from(MainActivity.this).inflate(R.layout.item_recyclerview, parent, false);            return new MyViewHolder(view);        }        @Override        public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {            MyViewHolder myViewHolder = (MyViewHolder) holder;            myViewHolder.checkBox.setText(content.get(position));                        myViewHolder.checkBox.setOnCheckedChangeListener(null);//先设置一次CheckBox的选中监听器,传入参数null            myViewHolder.checkBox.setChecked(flag[position]);//用数组中的值设置CheckBox的选中状态                        //再设置一次CheckBox的选中监听器,当CheckBox的选中状态发生改变时,把改变后的状态储存在数组中            myViewHolder.checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {                @Override                public void onCheckedChanged(CompoundButton compoundButton, boolean b) {                    flag[position] = b;                }            });        }        @Override        public int getItemCount() {            content = new ArrayList<>();            for (int i = 0; i < 100; i++) {                content.add("CheckBox" + i);            }            return content.size();        }    }    private class MyViewHolder extends RecyclerView.ViewHolder {        private CheckBox checkBox;        public MyViewHolder(View itemView) {            super(itemView);            checkBox = (CheckBox) itemView.findViewById(R.id.id_check_box);        }    }}


比较这两段代码,我们会发现,首先我们定义了一个长度为100的数组,然后设置CheckBox的选中监听器,把null作为参数传进去,然后用数组中的值设置对应CheckBox的选中状态,最后再一次设置CheckBox的选中监听器,把CheckBox的选中状态储存在数组中的相应位置中。再次运行,发现问题已解决。






下面我们来讨论一下,如果要在RecyclerView的外面再添加一个CheckBox,用外面的CheckBox来控制RecyclerView中的CheckBox的全选和取消全选,要如何实现呢?


其实也很简单,只要用这个CheckBox来控制之前所定义的数组的指就好了。


首先来修改一下activity_main,代码如下:

<?xml version="1.0" encoding="utf-8"?><LinearLayout    xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="vertical">        <LinearLayout        android:layout_width="match_parent"        android:layout_height="60dp"        android:gravity="center_vertical">        <CheckBox            android:id="@+id/id_select_all"            android:layout_width="match_parent"            android:layout_height="wrap_content"            android:layout_marginLeft="10dp"            android:text="SelectAll"/>    </LinearLayout>    <android.support.v7.widget.RecyclerView        android:id="@+id/id_recycler_view"        android:layout_width="match_parent"        android:layout_height="match_parent">    </android.support.v7.widget.RecyclerView></LinearLayout>


我们在RecyclerView的外面添加了一个CheckBox,用这个CheckBox来控制RecyclerView中的CheckBox的全选,接下来修改MainActivity:

public class MainActivity extends AppCompatActivity {    private CheckBox selectAll;    private RecyclerView recyclerView;    private MyAdapter myAdapter;    private boolean []flag;//把flag数组定义为全局变量    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        selectAll = (CheckBox) findViewById(R.id.id_select_all);        recyclerView = (RecyclerView) findViewById(R.id.id_recycler_view);        flag = new boolean[100];//初始化flag        myAdapter = new MyAdapter();        recyclerView.setLayoutManager(new LinearLayoutManager(this));        recyclerView.setAdapter(myAdapter);        //设置外面CheckBox的选中监听器,把它的选中状态赋值给其他的所有CheckBox,然后更新RecyclerView的Adapter        selectAll.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {            @Override            public void onCheckedChanged(CompoundButton compoundButton, boolean b) {                for (int i = 0; i < 100; i++) {                    flag[i] = b;                }                myAdapter.notifyDataSetChanged();            }        });    }    private class MyAdapter extends RecyclerView.Adapter {        private List<String> content;//        private boolean[] flag = new boolean[100];        @Override        public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {            View view = LayoutInflater.from(MainActivity.this).inflate(R.layout.item_recyclerview, parent, false);            return new MyViewHolder(view);        }        @Override        public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {            MyViewHolder myViewHolder = (MyViewHolder) holder;            myViewHolder.checkBox.setText(content.get(position));            myViewHolder.checkBox.setOnCheckedChangeListener(null);            myViewHolder.checkBox.setChecked(flag[position]);            myViewHolder.checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {                @Override                public void onCheckedChanged(CompoundButton compoundButton, boolean b) {                    flag[position] = b;                }            });        }        @Override        public int getItemCount() {            content = new ArrayList<>();            for (int i = 0; i < 100; i++) {                content.add("CheckBox" + i);            }            return content.size();        }    }    private class MyViewHolder extends RecyclerView.ViewHolder {        private CheckBox checkBox;        public MyViewHolder(View itemView) {            super(itemView);            checkBox = (CheckBox) itemView.findViewById(R.id.id_check_box);        }    }}


这里我们先把记录CheckBox选中状态的数组定义为全局变量,然后设置外面的CheckBox的监听器,把它的选中状态赋值给其他的所有CheckBox,紧接着更新一下RecyclerView的Adapter就可以了




这里我们在讨论一下RecyclerView的另外一个问题,就是当要删除某个子项的时候会出现删除紊乱的情况,为了说明这个问题,我们先来尝试实践一下,修改item_recyclerview:

<?xml version="1.0" encoding="utf-8"?><RelativeLayout    xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="60dp"    android:gravity="center_vertical">    <CheckBox        android:id="@+id/id_check_box"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:layout_marginLeft="10dp"        android:text="CheckBox"/>        <Button        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_alignParentRight="true"        android:layout_marginRight="10dp"        android:text="delete"/></RelativeLayout>

我们增加了一个Button,接下来设置这个Button,当它被点击的时候就删除它所在位置的item,修改MainActivity如下,主要修改的是Adapter部分,其他部分的代码就不贴了:

    private class MyAdapter extends RecyclerView.Adapter {//        private List<String> content;//        private boolean[] flag = new boolean[100];        @Override        public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {            View view = LayoutInflater.from(MainActivity.this).inflate(R.layout.item_recyclerview, parent, false);            return new MyViewHolder(view);        }        @Override        public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {            final MyViewHolder myViewHolder = (MyViewHolder) holder;            myViewHolder.checkBox.setText(content.get(position));            myViewHolder.checkBox.setOnCheckedChangeListener(null);            myViewHolder.checkBox.setChecked(flag[position]);            myViewHolder.checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {                @Override                public void onCheckedChanged(CompoundButton compoundButton, boolean b) {                    flag[position] = b;                }            });            //设置监听器,当按钮被点击是,删除它所在的item            myViewHolder.button.setOnClickListener(new View.OnClickListener() {                @Override                public void onClick(View view) {                    content.remove(position);                    notifyItemRemoved(position);                }            });        }        @Override        public int getItemCount() {//            content = new ArrayList<>();//            for (int i = 0; i < 100; i++) {//                content.add("CheckBox" + i);//            }            return content.size();        }    }    private class MyViewHolder extends RecyclerView.ViewHolder {        private CheckBox checkBox;        private Button button;//定义删除按钮        public MyViewHolder(View itemView) {            super(itemView);            checkBox = (CheckBox) itemView.findViewById(R.id.id_check_box);            button = (Button) itemView.findViewById(R.id.id_delete);        }    }

这是点击Button,我们会发现,问题出现了,第一次可以正常删除,第二次删除的item却是我们点击的Button所在的下一个item,后面的删除也会各种混乱,这是因为函数里面的传入的参数position,它是在进行onBind操作时确定的,在删除单项后,已经出现在画面里的项不会再有调用onBind机会,这样它保留的position一直是未进行删除操作前的postion值,对于尚未进入画面的单项来说,它会使用新的position值(好吧这段是抄的,其实我也不太懂啥意思),解决方法如下:

myViewHolder.button.setOnClickListener(new View.OnClickListener() {    @Override    public void onClick(View view) {        content.remove(position);        notifyItemRemoved(position);        notifyItemRangeChanged(position, content.size());//对于被删掉的位置及其后range大小范围内的view进行重新onBindViewHolder    }});

只要加一行代码就好了,这行代码的作用就是对于被删掉的位置及其后range大小范围内的view进行重新onBindViewHolder



此项目已上传到githut:点击打开链接





0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 开车有人别车怎么办 判刑罚金交不起怎么办 罚金太多交不起怎么办 判决书上的罚金怎么办 刑事罚款不交怎么办 缎面的鞋脏了怎么办 夏天脸上长粉刺怎么办 鬃狮不吃东西怎么办 买二手车的车牌怎么办 榴莲打开了肉没熟怎么办 gmat的prep做完怎么办 电脑没ip地址怎么办 电脑ip地址缺失怎么办 cos还原不了人物怎么办 执行局抓人十五天不放人怎么办 笔记本电脑键盘按键错乱怎么办 靠墙倒立上不去怎么办 医院多收钱了怎么办 学生总是转笔怎么办 吃错东西呕吐怎么办 手腕筋扭伤了怎么办 右膝盖内侧疼痛怎么办 膝盖关节腔积液怎么办 小孩玩游戏花钱怎么办 初三玩手机上瘾怎么办 无线网有感叹号怎么办 台式电脑网络感叹号怎么办 忘记发红包密码怎么办 电信诈骗被骗后怎么办 C小孩讨厌上学怎么办 军训来大姨妈怎么办 夏天军训来月经怎么办 军训遇到大姨妈怎么办 长春市图书馆读者证怎么办 济宁图书馆怎么办读者卡 海南省图书馆读者证怎么办 小孩子特别能吃怎么办 小孩吃不下去药怎么办 小孩不肯去上学怎么办 初中没有团员证怎么办 入团申请书丢了怎么办