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

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

2016年08月28日 18:24:24 Zackratos 阅读数 6289

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。

本文链接:https://blog.csdn.net/Zackratos/article/details/52346986

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

 

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

 

 

 

然后 开始编辑activity_main:

 

 
  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空间,代码如下:

 

 
  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等,过程不再详细叙述,编辑后的代码如下:

 

 
  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滚动到屏幕上的时候,再用数组或列表中对应的值把它的选中状态改回来就好了,修改后的代码如下:

 

 
  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,代码如下:

 

 
  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:

 

 
  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:

 

 
  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部分,其他部分的代码就不贴了:

 

 

 
  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值(好吧这段是抄的,其实我也不太懂啥意思),解决方法如下:

 

 
  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:点击打开链接


 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值