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

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


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





然后 开始编辑activity_main:

[html]  view plain  copy
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout   
  3.     xmlns:android="http://schemas.android.com/apk/res/android"  
  4.     android:layout_width="match_parent"  
  5.     android:layout_height="match_parent"  
  6.     android:orientation="vertical">  
  7.       
  8.     <android.support.v7.widget.RecyclerView  
  9.         android:id="@+id/id_recycler_view"  
  10.         android:layout_width="match_parent"  
  11.         android:layout_height="match_parent">  
  12.           
  13.     </android.support.v7.widget.RecyclerView>  
  14. </LinearLayout>  



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

[html]  view plain  copy
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout   
  3.     xmlns:android="http://schemas.android.com/apk/res/android"  
  4.     android:orientation="vertical"   
  5.     android:layout_width="match_parent"  
  6.     android:layout_height="60dp"  
  7.     android:gravity="center_vertical">  
  8.   
  9.     <CheckBox  
  10.         android:id="@+id/id_check_box"  
  11.         android:layout_width="match_parent"  
  12.         android:layout_height="wrap_content"  
  13.         android:layout_marginLeft="10dp"  
  14.         android:text="CheckBox"/>  
  15. </LinearLayout>  



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

[java]  view plain  copy
  1. public class MainActivity extends AppCompatActivity {  
  2.   
  3.     private RecyclerView recyclerView;  
  4.     private MyAdapter myAdapter;  
  5.   
  6.     @Override  
  7.     protected void onCreate(Bundle savedInstanceState) {  
  8.         super.onCreate(savedInstanceState);  
  9.         setContentView(R.layout.activity_main);  
  10.   
  11.         recyclerView = (RecyclerView) findViewById(R.id.id_recycler_view);  
  12.         myAdapter = new MyAdapter();  
  13.         recyclerView.setLayoutManager(new LinearLayoutManager(this));  
  14.         recyclerView.setAdapter(myAdapter);  
  15.     }  
  16.   
  17.   
  18.   
  19.     private class MyAdapter extends RecyclerView.Adapter {  
  20.   
  21.         private List<String> content;  
  22.   
  23.         @Override  
  24.         public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {  
  25.             View view = LayoutInflater.from(MainActivity.this).inflate(R.layout.item_recyclerview, parent, false);  
  26.             return new MyViewHolder(view);  
  27.         }  
  28.   
  29.         @Override  
  30.         public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {  
  31.             MyViewHolder myViewHolder = (MyViewHolder) holder;  
  32.             myViewHolder.checkBox.setText(content.get(position));  
  33.   
  34.         }  
  35.   
  36.         @Override  
  37.         public int getItemCount() {  
  38.             content = new ArrayList<>();  
  39.             for (int i = 0; i < 100; i++) {  
  40.                 content.add("CheckBox" + i);  
  41.             }  
  42.             return content.size();  
  43.         }  
  44.     }  
  45.   
  46.   
  47.     private class MyViewHolder extends RecyclerView.ViewHolder {  
  48.   
  49.         private CheckBox checkBox;  
  50.   
  51.         public MyViewHolder(View itemView) {  
  52.             super(itemView);  
  53.             checkBox = (CheckBox) itemView.findViewById(R.id.id_check_box);  
  54.         }  
  55.     }  
  56. }  


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

[java]  view plain  copy
  1. public class MainActivity extends AppCompatActivity {  
  2.   
  3.     private RecyclerView recyclerView;  
  4.     private MyAdapter myAdapter;  
  5.   
  6.     @Override  
  7.     protected void onCreate(Bundle savedInstanceState) {  
  8.         super.onCreate(savedInstanceState);  
  9.         setContentView(R.layout.activity_main);  
  10.   
  11.         recyclerView = (RecyclerView) findViewById(R.id.id_recycler_view);  
  12.         myAdapter = new MyAdapter();  
  13.         recyclerView.setLayoutManager(new LinearLayoutManager(this));  
  14.         recyclerView.setAdapter(myAdapter);  
  15.     }  
  16.   
  17.   
  18.   
  19.     private class MyAdapter extends RecyclerView.Adapter {  
  20.   
  21.         private List<String> content;  
  22.         private boolean[] flag = new boolean[100];//此处添加一个boolean类型的数组  
  23.   
  24.         @Override  
  25.         public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {  
  26.             View view = LayoutInflater.from(MainActivity.this).inflate(R.layout.item_recyclerview, parent, false);  
  27.             return new MyViewHolder(view);  
  28.         }  
  29.   
  30.         @Override  
  31.         public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {  
  32.             MyViewHolder myViewHolder = (MyViewHolder) holder;  
  33.             myViewHolder.checkBox.setText(content.get(position));  
  34.               
  35.             myViewHolder.checkBox.setOnCheckedChangeListener(null);//先设置一次CheckBox的选中监听器,传入参数null  
  36.             myViewHolder.checkBox.setChecked(flag[position]);//用数组中的值设置CheckBox的选中状态  
  37.               
  38.             //再设置一次CheckBox的选中监听器,当CheckBox的选中状态发生改变时,把改变后的状态储存在数组中  
  39.             myViewHolder.checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {  
  40.                 @Override  
  41.                 public void onCheckedChanged(CompoundButton compoundButton, boolean b) {  
  42.                     flag[position] = b;  
  43.                 }  
  44.             });  
  45.         }  
  46.   
  47.         @Override  
  48.         public int getItemCount() {  
  49.             content = new ArrayList<>();  
  50.             for (int i = 0; i < 100; i++) {  
  51.                 content.add("CheckBox" + i);  
  52.             }  
  53.             return content.size();  
  54.         }  
  55.     }  
  56.   
  57.   
  58.     private class MyViewHolder extends RecyclerView.ViewHolder {  
  59.   
  60.         private CheckBox checkBox;  
  61.   
  62.         public MyViewHolder(View itemView) {  
  63.             super(itemView);  
  64.             checkBox = (CheckBox) itemView.findViewById(R.id.id_check_box);  
  65.         }  
  66.     }  
  67. }  


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






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


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


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

[html]  view plain  copy
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout  
  3.     xmlns:android="http://schemas.android.com/apk/res/android"  
  4.     android:layout_width="match_parent"  
  5.     android:layout_height="match_parent"  
  6.     android:orientation="vertical">  
  7.       
  8.     <LinearLayout  
  9.         android:layout_width="match_parent"  
  10.         android:layout_height="60dp"  
  11.         android:gravity="center_vertical">  
  12.         <CheckBox  
  13.             android:id="@+id/id_select_all"  
  14.             android:layout_width="match_parent"  
  15.             android:layout_height="wrap_content"  
  16.             android:layout_marginLeft="10dp"  
  17.             android:text="SelectAll"/>  
  18.     </LinearLayout>  
  19.   
  20.     <android.support.v7.widget.RecyclerView  
  21.         android:id="@+id/id_recycler_view"  
  22.         android:layout_width="match_parent"  
  23.         android:layout_height="match_parent">  
  24.   
  25.     </android.support.v7.widget.RecyclerView>  
  26. </LinearLayout>  


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

[java]  view plain  copy
  1. public class MainActivity extends AppCompatActivity {  
  2.   
  3.     private CheckBox selectAll;  
  4.     private RecyclerView recyclerView;  
  5.     private MyAdapter myAdapter;  
  6.     private boolean []flag;//把flag数组定义为全局变量  
  7.   
  8.     @Override  
  9.     protected void onCreate(Bundle savedInstanceState) {  
  10.         super.onCreate(savedInstanceState);  
  11.         setContentView(R.layout.activity_main);  
  12.   
  13.         selectAll = (CheckBox) findViewById(R.id.id_select_all);  
  14.         recyclerView = (RecyclerView) findViewById(R.id.id_recycler_view);  
  15.         flag = new boolean[100];//初始化flag  
  16.         myAdapter = new MyAdapter();  
  17.         recyclerView.setLayoutManager(new LinearLayoutManager(this));  
  18.         recyclerView.setAdapter(myAdapter);  
  19.   
  20.         //设置外面CheckBox的选中监听器,把它的选中状态赋值给其他的所有CheckBox,然后更新RecyclerView的Adapter  
  21.         selectAll.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {  
  22.             @Override  
  23.             public void onCheckedChanged(CompoundButton compoundButton, boolean b) {  
  24.                 for (int i = 0; i < 100; i++) {  
  25.                     flag[i] = b;  
  26.                 }  
  27.                 myAdapter.notifyDataSetChanged();  
  28.             }  
  29.         });  
  30.     }  
  31.   
  32.   
  33.   
  34.     private class MyAdapter extends RecyclerView.Adapter {  
  35.   
  36.         private List<String> content;  
  37. //        private boolean[] flag = new boolean[100];  
  38.   
  39.         @Override  
  40.         public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {  
  41.             View view = LayoutInflater.from(MainActivity.this).inflate(R.layout.item_recyclerview, parent, false);  
  42.             return new MyViewHolder(view);  
  43.         }  
  44.   
  45.         @Override  
  46.         public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {  
  47.             MyViewHolder myViewHolder = (MyViewHolder) holder;  
  48.             myViewHolder.checkBox.setText(content.get(position));  
  49.             myViewHolder.checkBox.setOnCheckedChangeListener(null);  
  50.             myViewHolder.checkBox.setChecked(flag[position]);  
  51.             myViewHolder.checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {  
  52.                 @Override  
  53.                 public void onCheckedChanged(CompoundButton compoundButton, boolean b) {  
  54.                     flag[position] = b;  
  55.                 }  
  56.             });  
  57.   
  58.   
  59.         }  
  60.   
  61.         @Override  
  62.         public int getItemCount() {  
  63.             content = new ArrayList<>();  
  64.             for (int i = 0; i < 100; i++) {  
  65.                 content.add("CheckBox" + i);  
  66.             }  
  67.             return content.size();  
  68.         }  
  69.     }  
  70.   
  71.   
  72.     private class MyViewHolder extends RecyclerView.ViewHolder {  
  73.   
  74.         private CheckBox checkBox;  
  75.   
  76.         public MyViewHolder(View itemView) {  
  77.             super(itemView);  
  78.             checkBox = (CheckBox) itemView.findViewById(R.id.id_check_box);  
  79.         }  
  80.     }  
  81. }  


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




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

[html]  view plain  copy
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <RelativeLayout  
  3.     xmlns:android="http://schemas.android.com/apk/res/android"  
  4.     android:layout_width="match_parent"  
  5.     android:layout_height="60dp"  
  6.     android:gravity="center_vertical">  
  7.   
  8.     <CheckBox  
  9.         android:id="@+id/id_check_box"  
  10.         android:layout_width="match_parent"  
  11.         android:layout_height="wrap_content"  
  12.         android:layout_marginLeft="10dp"  
  13.         android:text="CheckBox"/>  
  14.       
  15.     <Button  
  16.         android:layout_width="wrap_content"  
  17.         android:layout_height="wrap_content"  
  18.         android:layout_alignParentRight="true"  
  19.         android:layout_marginRight="10dp"  
  20.         android:text="delete"/>  
  21. </RelativeLayout>  

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

[java]  view plain  copy
  1.     private class MyAdapter extends RecyclerView.Adapter {  
  2.   
  3. //        private List<String> content;  
  4. //        private boolean[] flag = new boolean[100];  
  5.   
  6.         @Override  
  7.         public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {  
  8.             View view = LayoutInflater.from(MainActivity.this).inflate(R.layout.item_recyclerview, parent, false);  
  9.             return new MyViewHolder(view);  
  10.         }  
  11.   
  12.         @Override  
  13.         public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {  
  14.             final MyViewHolder myViewHolder = (MyViewHolder) holder;  
  15.             myViewHolder.checkBox.setText(content.get(position));  
  16.             myViewHolder.checkBox.setOnCheckedChangeListener(null);  
  17.             myViewHolder.checkBox.setChecked(flag[position]);  
  18.             myViewHolder.checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {  
  19.                 @Override  
  20.                 public void onCheckedChanged(CompoundButton compoundButton, boolean b) {  
  21.                     flag[position] = b;  
  22.                 }  
  23.             });  
  24.             //设置监听器,当按钮被点击是,删除它所在的item  
  25.             myViewHolder.button.setOnClickListener(new View.OnClickListener() {  
  26.                 @Override  
  27.                 public void onClick(View view) {  
  28.                     content.remove(position);  
  29.                     notifyItemRemoved(position);  
  30.                 }  
  31.             });  
  32.   
  33.   
  34.         }  
  35.   
  36.         @Override  
  37.         public int getItemCount() {  
  38. //            content = new ArrayList<>();  
  39. //            for (int i = 0; i < 100; i++) {  
  40. //                content.add("CheckBox" + i);  
  41. //            }  
  42.             return content.size();  
  43.         }  
  44.     }  
  45.   
  46.   
  47.     private class MyViewHolder extends RecyclerView.ViewHolder {  
  48.   
  49.         private CheckBox checkBox;  
  50.         private Button button;//定义删除按钮  
  51.   
  52.         public MyViewHolder(View itemView) {  
  53.             super(itemView);  
  54.             checkBox = (CheckBox) itemView.findViewById(R.id.id_check_box);  
  55.             button = (Button) itemView.findViewById(R.id.id_delete);  
  56.         }  
  57.     }  

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

[java]  view plain  copy
  1. myViewHolder.button.setOnClickListener(new View.OnClickListener() {  
  2.     @Override  
  3.     public void onClick(View view) {  
  4.         content.remove(position);  
  5.         notifyItemRemoved(position);  
  6.         notifyItemRangeChanged(position, content.size());//对于被删掉的位置及其后range大小范围内的view进行重新onBindViewHolder  
  7.     }  
  8. });  

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



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

源自:http://blog.csdn.net/zackratos/article/details/52346986

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值