CheckBox en RecyclerView sigue verificando diferentes elementos
Aquí está el XML de mis artículos dentro de RecyclerView
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:id="@+id/cvItems"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:layout_margin="2dp"
card_view:cardElevation="0dp"
card_view:contentPadding="0dp"
card_view:cardBackgroundColor="#FFFFFF"
>
<LinearLayout
android:orientation="horizontal"
android:layout_height="fill_parent"
android:layout_width="fill_parent">
<TextView
android:layout_width="0dip"
android:layout_height="match_parent"
android:layout_weight="0.8"
android:id="@+id/tvContent"
android:textSize="15dp"
android:paddingLeft="5dp"
android:paddingRight="5dp" />
<CheckBox
android:id="@+id/cbSelect"
android:layout_width="0dip"
android:layout_weight="0.2"
android:layout_height="match_parent"
android:button="@drawable/cb_checked"
android:gravity="center_horizontal"
android:textAlignment="center"
android:layout_gravity="center_horizontal" />
</LinearLayout>
</android.support.v7.widget.CardView>
Y aquí está el adaptador RecyclerView que amplía el diseño anterior para cada uno de sus elementos:
public class AdapterTrashIncome extends RecyclerView.Adapter<AdapterTrashIncome.ViewHolder> {
private ArrayList<ObjectIncome> myItems = new ArrayList<>();
public AdapterTrashIncome(ArrayList<ObjectIncome> getItems, Context context){
try {
mContext = context;
myItems = getItems;
}catch (Exception e){
Log.e(FILE_NAME, "51: " + e.toString());
e.printStackTrace();
}
}
public class ViewHolder extends RecyclerView.ViewHolder {
public TextView tvContent;
public CheckBox cbSelect;
public ViewHolder(View v) {
super(v);
tvContent = (TextView) v.findViewById(R.id.tvContent);
cbSelect = (CheckBox) v.findViewById(R.id.cbSelect);
}
}
@Override
public void onBindViewHolder(ViewHolder holder, final int position) {
final ObjectIncome objIncome = myItems.get(position);
String content = "<b>lalalla</b>";
holder.tvContent.setText(Html.fromHtml(content));
}
}
El problema es que digamos que tengo 10 elementos dentro de RecyclerView. Cuando marqué la casilla de verificación en el elemento 1,2,3, me desplacé hacia abajo en RecyclerView y de repente algunos de los otros elementos, por ejemplo, los elementos 8,9, están marcados. Y cuando me desplazo hacia arriba nuevamente, los elementos 1 y 3 están marcados pero no el elemento 2. ¿Alguna idea de por qué sucede esto?
Ese es un comportamiento esperado. No estás configurando tu casilla de verificación seleccionada o no. Estás seleccionando uno y el titular de la vista lo mantiene seleccionado. Puede agregar una variable booleana a su objeto ObjectIncome y mantener el estado de selección de su artículo.
Puedes mirar mi ejemplo. Puedes hacer algo como eso:
public class AdapterTrashIncome extends RecyclerView.Adapter<AdapterTrashIncome.ViewHolder> {
private ArrayList<ObjectIncome> myItems = new ArrayList<>();
public AdapterTrashIncome(ArrayList<ObjectIncome> getItems, Context context){
try {
mContext = context;
myItems = getItems;
}catch (Exception e){
Log.e(FILE_NAME, "51: " + e.toString());
e.printStackTrace();
}
}
public class ViewHolder extends RecyclerView.ViewHolder {
public TextView tvContent;
public CheckBox cbSelect;
public ViewHolder(View v) {
super(v);
tvContent = (TextView) v.findViewById(R.id.tvContent);
cbSelect = (CheckBox) v.findViewById(R.id.cbSelect);
}
}
@Override
public void onBindViewHolder(ViewHolder holder, final int position) {
final ObjectIncome objIncome = myItems.get(position);
String content = "<b>lalalla</b>";
holder.tvContent.setText(Html.fromHtml(content));
//in some cases, it will prevent unwanted situations
holder.cbSelect.setOnCheckedChangeListener(null);
//if true, your checkbox will be selected, else unselected
holder.cbSelect.setChecked(objIncome.isSelected());
holder.cbSelect.setOnCheckedChangeListener(new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
//set your object's last status
objIncome.setSelected(isChecked);
}
});
}
}
En resumen, ¡es por reciclar las vistas y usarlas nuevamente!
¿Cómo puedes evitar eso?
1.Marque onBindViewHolder
si debe marcar o desmarcar las casillas. no olvides poner ambos if y else
if (...)
holder.cbSelect.setChecked(true);
else
holder.cbSelect.setChecked(false);
- ¡Ponga un oyente para la casilla de verificación! Cada vez que sus estatuas marcadas cambien, actualice también el objeto correspondiente en su
myItems
matriz. entonces, cada vez que se muestra una nueva vista, se lee la estatua más nueva del objeto.
Simplemente agregue dos métodos de anulación deRecyclerView
@Override
public long getItemId(int position) {
return position;
}
@Override
public int getItemViewType(int position) {
return position;
}
Úselo solo si tiene un número limitado de elementos en su RecyclerView.
Intenté usar boolean
el valor en el modelo y mantener el CheckBox
estado, pero en mi caso no ayudó. Lo que funcionó para mí esthis.setIsRecyclable(false);
public class ComponentViewHolder extends RecyclerView.ViewHolder {
public MyViewHolder(View itemView) {
super(itemView);
...
this.setIsRecyclable(false);
}
Puede encontrar más explicaciones sobre esto aquí.
NOTA: Esta es una solución alternativa. Para utilizarlo correctamente, puede consultar el documento que establece
Las llamadas a setIsRecyclable() siempre deben estar emparejadas (una llamada a setIsRecyclabe(false) siempre debe coincidir con una llamada posterior a setIsRecyclable(true)). Se pueden anidar pares de llamadas, ya que el estado se cuenta internamente por referencia.
No sé cómo hacer esto en código, si alguien puede proporcionar más código sobre esto.