引子
今天,使用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,就醬紫。