引子
今天,使用RecyclerView + Checkbox的时候,发生了checkbox状态错乱的问题。
RecyclerView 为了提高效率,使用了Recycler回收机制,它的作用就是,不会产生多余的itemView,如果产生了向上滑动,就将最上方的itemView保存起来,然后接到最下面,然后重新加载数据(onBindViewHolder会被调用)。
但是这种方式,如果itemView中有checkbox,要继承之前的勾选状态,那就坑了爹了。因为重用的itemView会保留之前的check状态。
必须要想办法,标记它的勾选状态,才能正常显示勾选。
代码
解决此问题,最重要的代码在adapter里面。红色的,是关键代码.
主要思路: 利用一组map来记录每一个checkbox的选中状态,然后在checkbox的选中事件中,对这个map进行维护。具体的,请看下面代码。
1 import android.content.Context; 2 import android.support.annotation.NonNull; 3 import android.support.v7.widget.RecyclerView; 4 import android.view.LayoutInflater; 5 import android.view.View; 6 import android.view.ViewGroup; 7 import android.widget.CheckBox; 8 import android.widget.CompoundButton; 9 import android.widget.TextView; 10 11 import java.util.ArrayList; 12 import java.util.HashMap; 13 import java.util.List; 14 import java.util.Map; 15 16 public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> { 17 18 private Context context; 19 20 //先造一组模拟数据 21 private List<String> listData; 22 23 private Map<Integer, Boolean> checkStatus;//用来记录所有checkbox的状态 24 25 public MyAdapter(Context context) { 26 this.context = context; 27 initData(); 28 } 29 30 private void initData() { 31 listData = new ArrayList<>(); 32 for (int i = 0; i < 100; i++) { 33 listData.add("testData" + i); 34 } 35 36 checkStatus = new HashMap<>(); 37 for (int i = 0; i < listData.size(); i++) { 38 checkStatus.put(i, false);// 默认所有的checkbox都是没选中 39 } 40 41 } 42 43 public class MyViewHolder extends RecyclerView.ViewHolder { 44 CheckBox checkbox; 45 TextView textView; 46 47 public MyViewHolder(View itemView) { 48 super(itemView); 49 checkbox = itemView.findViewById(R.id.checkbox); 50 textView = itemView.findViewById(R.id.textView); 51 } 52 } 53 54 @NonNull 55 @Override 56 public MyAdapter.MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { 57 View v = LayoutInflater.from(context).inflate(R.layout.layout, parent, false); 58 MyViewHolder holder = new MyViewHolder(v); 59 return holder; 60 } 61 62 @Override 63 public void onBindViewHolder(@NonNull MyAdapter.MyViewHolder holder, final int position) { 64 holder.textView.setText(listData.get(position)); 65 holder.checkbox.setOnCheckedChangeListener(null);//清掉监听器 66 holder.checkbox.setChecked(checkStatus.get(position));//设置选中状态 67 holder.checkbox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {//再设置监听器 68 @Override 69 public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 70 checkStatus.put(position, isChecked);//check状态一旦改变,保存的check值也要发生相应的变化 71 } 72 }); 73 } 74 75 @Override 76 public int getItemCount() { 77 if (listData == null) return 0; 78 return listData.size(); 79 } 80 }
下面给出其他代码:
MainActivity.java
1 package com.example.recyclerview2; 2 3 import android.support.v7.app.AppCompatActivity; 4 import android.os.Bundle; 5 import android.support.v7.widget.LinearLayoutManager; 6 import android.support.v7.widget.RecyclerView; 7 8 public class MainActivity extends AppCompatActivity { 9 10 private RecyclerView rv_1; 11 12 @Override 13 protected void onCreate(Bundle savedInstanceState) { 14 super.onCreate(savedInstanceState); 15 setContentView(R.layout.activity_main); 16 17 rv_1 = findViewById(R.id.rv_1); 18 RecyclerView.LayoutManager manager = new LinearLayoutManager(this); 19 rv_1.setLayoutManager(manager); 20 MyAdapter adapter = new MyAdapter(this); 21 rv_1.setAdapter(adapter); 22 } 23 24 25 }
activity_main.xml
1 <?xml version="1.0" encoding="utf-8"?> 2 <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 xmlns:tools="http://schemas.android.com/tools" 4 android:layout_width="match_parent" 5 android:layout_height="match_parent" 6 tools:context=".MainActivity"> 7 8 <android.support.v7.widget.RecyclerView 9 android:id="@+id/rv_1" 10 android:layout_width="match_parent" 11 android:layout_height="match_parent"></android.support.v7.widget.RecyclerView> 12 13 </android.support.constraint.ConstraintLayout>
layout.xml
1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:layout_width="match_parent" 4 android:layout_height="wrap_content" 5 android:gravity="center" 6 android:orientation="horizontal"> 7 8 <TextView 9 android:id="@+id/textView" 10 android:layout_width="wrap_content" 11 android:layout_height="wrap_content" 12 android:layout_margin="10dp" 13 android:text="111" /> 14 15 <CheckBox 16 android:id="@+id/checkbox" 17 android:layout_width="wrap_content" 18 android:layout_height="wrap_content" 19 android:layout_margin="10dp" /> 20 21 </LinearLayout>
结语:
这里有一句代码很精髓,那就是
holder.checkbox.setOnCheckedChangeListener(null);//清掉监听器
Checkbox的监听器有一个特性,无论你是认为操作,还是用api来setChecked,都会触发监听器。所以,在itemView重新加载好之前,不要设置checkbox的check事件监听,否则会出现另外的混乱情况,有兴趣的可以试试看。
问题已解决,OK,就酱紫。