CheckBox en RecyclerView sigue verificando diferentes elementos

Resuelto imin asked hace 54 años • 27 respuestas

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?

imin avatar Jan 01 '70 08:01 imin
Aceptado

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);
            }
        });

    }
}
Oğuzhan Döngül avatar Sep 06 '2015 20:09 Oğuzhan Döngül

En resumen, ¡es por reciclar las vistas y usarlas nuevamente!

¿Cómo puedes evitar eso?

1.Marque onBindViewHoldersi debe marcar o desmarcar las casillas. no olvides poner ambos if y else

if (...)
    holder.cbSelect.setChecked(true);
else
    holder.cbSelect.setChecked(false);
  1. ¡Ponga un oyente para la casilla de verificación! Cada vez que sus estatuas marcadas cambien, actualice también el objeto correspondiente en su myItemsmatriz. entonces, cada vez que se muestra una nueva vista, se lee la estatua más nueva del objeto.
Omid Heshmatinia avatar Sep 06 '2015 20:09 Omid Heshmatinia

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;
}
Harish Reddy avatar Sep 06 '2017 04:09 Harish Reddy

Úselo solo si tiene un número limitado de elementos en su RecyclerView.

Intenté usar booleanel valor en el modelo y mantener el CheckBoxestado, 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.

Rana Ranvijay Singh avatar Jun 17 '2016 08:06 Rana Ranvijay Singh