CheckBox en RecyclerView sigue verificando diferentes artículos

Aquí está el XML para mis artículos dentro de RecyclerView

      

Y aquí está el adaptador RecyclerView que infle el diseño anterior para cada uno de sus elementos:

 public class AdapterTrashIncome extends RecyclerView.Adapter { private ArrayList myItems = new ArrayList(); public AdapterTrashIncome(ArrayList 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 = "lalalla"; holder.tvContent.setText(Html.fromHtml(content)); } } 

El problema es, digamos que tengo 10 elementos dentro de RecyclerView. Cuando marqué la checkbox en el ítem 1,2,3, me desplacé hacia abajo en RecyclerView; de repente, se verifican algunos de los otros ítems, por ejemplo, los ítems 8,9. Y cuando me desplazo hacia arriba nuevamente, los ítems 1 y 3 están marcados, pero no el ítem 2. ¿Alguna idea de por qué sucede esto?

Es normal. No está configurando su checkbox seleccionada o no. Está seleccionando uno y el titular de la vista lo mantiene seleccionado. Puede agregar una variable booleana en su objeto ObjectIncome y mantener el estado de selección de su elemento.

Puedes mirar mi ejemplo. Puedes hacer algo como eso:

 public class AdapterTrashIncome extends RecyclerView.Adapter { private ArrayList myItems = new ArrayList<>(); public AdapterTrashIncome(ArrayList 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 = "lalalla"; 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); } }); } } 

Traté de usar el valor booleano en el modelo y mantener el estado de la checkbox, pero no ayudó en mi caso. Lo que funcionó para mí es this.setIsRecyclable (false);

 public class ComponentViewHolder extends RecyclerView.ViewHolder { public MyViewHolder(View itemView) { super(itemView); .... this.setIsRecyclable(false); } 

Puede encontrar más información al respecto aquí https://developer.android.com/reference/android/support/v7/widget/RecyclerView.ViewHolder.html#isRecyclable ()

NOTA: Esto es una solución. Para usarlo correctamente, puede consultar el documento que dice “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 es internamente contado por referencia “. No sé cómo hacer esto en el código, si alguien puede proporcionar más código sobre esto.

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

¿Cómo puedes evitar eso?

1.En onBindViewHolder comprueba si debes marcar o desmarcar cuadros. no te olvides de poner ambos if y else

 if (...) holder.cbSelect.setChecked(true); else holder.cbSelect.setChecked(false); 
  1. Pon un oyente para la checkbox! siempre que sus estatuas marcadas cambien, ¡actualice también el objeto correspondiente en su matriz myItems ! así que cada vez que se muestra una nueva vista, lee la estatua más nueva del objeto.

Puede usar la clase Model para realizar un seguimiento de la checkbox de cada elemento recyclerView. La referencia completa es de: RecyclerView Checkbox Android

setTag y getTag se utilizan para realizar un seguimiento del estado de la checkbox. Verifique el enlace de referencia completo para más información. También enseña cómo enviar elementos marcados a NEXTACTIVITY .

Haz un modelo

 public class Model { private boolean isSelected; private String animal; public String getAnimal() { return animal; } public void setAnimal(String animal) { this.animal = animal; } public boolean getSelected() { return isSelected; } public void setSelected(boolean selected) { isSelected = selected; } } 

crear integer.xml

   1 2  

Finalmente el adaptador se ve así:

  import android.content.Context; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.CheckBox; import android.widget.TextView; import android.widget.Toast; import java.util.ArrayList; public class CustomAdapter extends RecyclerView.Adapter { private LayoutInflater inflater; public static ArrayList imageModelArrayList; private Context ctx; public CustomAdapter(Context ctx, ArrayList imageModelArrayList) { inflater = LayoutInflater.from(ctx); this.imageModelArrayList = imageModelArrayList; this.ctx = ctx; } @Override public CustomAdapter.MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = inflater.inflate(R.layout.rv_item, parent, false); MyViewHolder holder = new MyViewHolder(view); return holder; } @Override public void onBindViewHolder(final CustomAdapter.MyViewHolder holder, int position) { holder.checkBox.setText("Checkbox " + position); holder.checkBox.setChecked(imageModelArrayList.get(position).getSelected()); holder.tvAnimal.setText(imageModelArrayList.get(position).getAnimal()); // holder.checkBox.setTag(R.integer.btnplusview, convertView); holder.checkBox.setTag(position); holder.checkBox.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Integer pos = (Integer) holder.checkBox.getTag(); Toast.makeText(ctx, imageModelArrayList.get(pos).getAnimal() + " clicked!", Toast.LENGTH_SHORT).show(); if (imageModelArrayList.get(pos).getSelected()) { imageModelArrayList.get(pos).setSelected(false); } else { imageModelArrayList.get(pos).setSelected(true); } } }); } @Override public int getItemCount() { return imageModelArrayList.size(); } class MyViewHolder extends RecyclerView.ViewHolder { protected CheckBox checkBox; private TextView tvAnimal; public MyViewHolder(View itemView) { super(itemView); checkBox = (CheckBox) itemView.findViewById(R.id.cb); tvAnimal = (TextView) itemView.findViewById(R.id.animal); } } 

}

Solo agregue dos métodos de anulación de recyclerview

 @Override public long getItemId(int position) { return position; } @Override public int getItemViewType(int position) { return position; } 

Debe separar las interacciones onBindViewHolder (lógica) con CheckBox y las interacciones del usuario con la checkbox. Utilicé OnCheckedChangeListener para las interacciones del usuario (obviamente) y ViewHolder.bind () para la lógica, por eso es necesario configurar el oyente verificado como nulo antes de configurar el oyente y luego de que el titular esté listo: configure el oyente verificado para las interacciones del usuario.

 boolean[] checkedStatus = new boolean[numberOfRows]; @Override public void onBindViewHolder(final RecyclerView.ViewHolder holder, int position) { final ViewHolderItem itemHolder = (ViewHolderItem) holder; //holder.bind should not trigger onCheckedChanged, it should just update UI itemHolder.checkBox.setOnCheckedChangeListener(null); itemHolder.bind(position); itemHolder.checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { if (isChecked) { checkedStatus[holder.getAdapterPosition()] = true; performCheckedActions(); //your logic here } else { checkedStatus[holder.getAdapterPosition()] = false; performUncheckedActions(); //your logic here } } }); } public void bind(int position) { boolean checked = checkedStatus[position]; if (checked) { checkBox.setChecked(false); } else { checkBox.setChecked(true); } } 

El problema de esta solución que encontré es crear una matriz global estática y usarla en “ONBindViewHolder” ADAPER CLASS, en la que creé todos los elementos / objetos necesarios globales.

 public class RVAdapter extends RecyclerView.Adapter { private Context context; public static class PersonViewHolder extends RecyclerView.ViewHolder { CardView cv; TextView question,category; TextView personAge; ImageView upvote; Button b1; public static int k; private int visibleThreshold = 5; public static int i=0; static int check[]; //Static array PersonViewHolder(View itemView,int i) { super(itemView); if(i==PersonViewHolder.k) { b1=(Button)itemView.findViewById(R.id.loadmore); } else { cv = (CardView)itemView.findViewById(R.id.cv); question = (TextView)itemView.findViewById(R.id.question); category = (TextView)itemView.findViewById(R.id.text_categ); personAge = (TextView)itemView.findViewById(R.id.text1); upvote = (ImageView)itemView.findViewById(R.id.upvote); } } } 

Aquí (EN CONSTRUCTOR de RVADAPTER CLASS) dí tamaño a la matriz igual al tamaño de / no de los elementos que voy a mostrar en la vista de reciclador

 List persons; RVAdapter(List persons){ this.persons = persons; PersonViewHolder.check=new int[persons.size()]; PersonViewHolder.k=persons.size(); } 

BindViewHolder, I, Aplicado este concepto en un botón, cuando hago clic en un botón, la imagen de fondo del botón cambia. objeto de botón que utilicé son los nombres como “voto a favor”, como “i” mantiene la posición de cada artículo en la vista de reciclador, lo usé como un índice de matriz que funciona como indicador y que hace un seguimiento del estado de los elementos.

 @Override public void onBindViewHolder(final PersonViewHolder personViewHolder, final int i) { if(i==PersonViewHolder.k) { personViewHolder.b1.setText("load more"); } else { personViewHolder.question.setText(persons.get(i).name); personViewHolder.personAge.setText(persons.get(i).age); if(personViewHolder.check[i]==0) {personViewHolder.upvote.setBackgroundResource(R.drawable.noupvote); } else { personViewHolder.upvote.setBackgroundResource(R.drawable.upvote); } personViewHolder.upvote.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if(personViewHolder.check[i]==0) {personViewHolder.check[i]=1; personViewHolder.upvote.setBackgroundResource(R.drawable.upvote); } else {personViewHolder.check[i]=0; personViewHolder.upvote.setBackgroundResource(R.drawable.noupvote); } } }); // personViewHolder.personPhoto.setImageResource(persons.get(i).photoId); } } 

Tuve el mismo problema en una lista de RecyclerView con modificadores, y lo resolví con @oguzhand answer, pero con este código dentro de checkedChangeListener:

 if (buttonView.isPressed()) { if (isChecked) { group.setSelected(true); } else { group.setSelected(false); } }else{ if (isChecked) { buttonView.setChecked(false); } else { buttonView.setChecked(true); } } 

(Donde ‘grupo’ es la entidad que quiero seleccionar / deseleccionar)

He tenido el mismo problema. Cuando estaba haciendo clic en el botón de alternar del artículo en mi reciclador, el botón Alternar comprobado aparecía en cada décimo elemento (por ejemplo, si se hacía clic en un elemento con índice 0, también se hacía clic en los elementos con 9, 18, 27 índices). En primer lugar, mi código en onBindViewHolder era:

 if (newsItems.get(position).getBookmark() == 1) { holder.getToggleButtonBookmark().setChecked(true); } 

Pero luego agregué la statement de Else

 if (newsItems.get(position).getBookmark() == 1) { holder.getToggleButtonBookmark().setChecked(true); //else statement prevents auto toggling } else{ holder.getToggleButtonBookmark().setChecked(false); } 

Y el problema fue resuelto

de acuerdo, hay muchas respuestas aquí , publicaré mi código y simplemente explicaré lo que hice … quizás ayude a los jóvenes como yo: D.

1- Objetivo:

crearemos una lista de RecyclerView que tiene CheckBox y RadioButton , algo como esto:

enter image description here 2- Clase de modelo

 public class ModelClass { private String time; private boolean checked; private boolean free; private boolean paid; public TherapistScheduleModel(String time, boolean checked, boolean free, boolean paid) { this.time = time; this.checked = checked; this.free = free; this.paid = paid; } public boolean isFree() { return free; } public void setFree(boolean free) { this.free = free; } public boolean isPaid() { return paid; } public void setPaid(boolean paid) { this.paid = paid; } public String getTime() { return time; } public void setTime(String time) { this.time = time; } public boolean getChecked() { return checked; } public void setChecked(boolean checked) { this.checked= checked; } } 

3-My Amazing Adapter

 public class MyAdapter extends RecyclerView.Adapter { private Context context; private ListAllListeners listAllListeners; private ArrayList mDataList; public MyAdapter(Context context, ArrayList mDataList, ListAllListeners listAllListeners) { this.mDataList = mDataList; this.listAllListeners = listAllListeners; this.context = context; } @NonNull @Override public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { LayoutInflater inflater = LayoutInflater.from(parent.getContext()); View view = inflater.inflate(R.layout.single_view, parent, false); return new MyViewHolder(view); } @Override public int getItemCount() { if (mDataList != null) return mDataList.size(); else return 0; } @Override public void onBindViewHolder(@NonNull final MyViewHolder holder, final int position) { //important to: //setOnCheckedChangeListener to 'null' holder.checkBoxTime.setOnCheckedChangeListener(null); holder.freeRB.setOnCheckedChangeListener(null); holder.paidRB.setOnCheckedChangeListener(null); //Check Box holder.checkBoxTime.setText(mDataList.get(holder.getAdapterPosition()).getTime()); //here we check if the item is checked or not from the model. if(mDataList.get(holder.getAdapterPosition()).getChecked()) holder.checkBoxTime.setChecked(true); else holder.checkBoxTime.setChecked(false); holder.checkBoxTime.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton compoundButton, boolean b) { if (b) { mDataList.get(holder.getAdapterPosition()).setChecked(true); listAllListeners.onItemCheck(holder.checkBoxTime.getText().toString(), holder.getAdapterPosition()); } else { mDataList.get(holder.getAdapterPosition()).setChecked(false); listAllListeners.onItemUncheck(holder.checkBoxTime.getText().toString(), holder.getAdapterPosition()); } } }); //Radio Buttons if(mDataList.get(holder.getAdapterPosition()).isFree()) holder.freeRB.setChecked(true); else holder.freeRB.setChecked(false); holder.freeRB.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton compoundButton, boolean b) { if (b) { mDataList.get(holder.getAdapterPosition()).setFree(true); listAllListeners.onFreeCheck(holder.freeRB.getText().toString(), holder.getAdapterPosition()); } else { mDataList.get(holder.getAdapterPosition()).setFree(false); listAllListeners.onFreeUncheck(holder.freeRB.getText().toString(), holder.getAdapterPosition()); } } }); //***and so on to paidRB*** }//end onBindViewHolder() public interface ListAllListeners { //here is a list of clicked listeners to use them as you want ;). //you can get a list of checked or unChecked of all void onItemCheck(String checkBoxName, int position); void onItemUncheck(String checkBoxName, int position); void onFreeCheck(String name, int pos); void onFreeUncheck(String name, int pos); void onPaidCheck(String name, int pos); void onPaidUncheck(String name, int pos); } class MyViewHolder extends RecyclerView.ViewHolder { CheckBox checkBoxTime; RadioButton freeRB, paidRB; MyViewHolder(View itemView) { super(itemView); checkBoxTime = itemView.findViewById(R.id.timeCheckBox); freeRB = itemView.findViewById(R.id.freeRadioBtn); paidRB = itemView.findViewById(R.id.paidRadioBtn); } }//end class MyViewHolder }//end class 

3- En Activity obtienes algo como esto:

 myAdapter= new MyAdapter(getActivity().getApplicationContext(), mDataList, new MyAdapter.ListAllListeners() { @Override public void onItemCheck(String checkBoxName, int position) { Toast.makeText(getActivity(), "" + checkBoxName + " " + position, Toast.LENGTH_SHORT).show(); } @Override public void onItemUncheck(String checkBoxName, int position) { Toast.makeText(getActivity(), "" + checkBoxName + " " + position, Toast.LENGTH_SHORT).show(); } @Override public void onFreeCheck(String name, int position) { Toast.makeText(getActivity(), "" + name + " " + position, Toast.LENGTH_SHORT).show(); } @Override public void onFreeUncheck(String name, int position) { Toast.makeText(getActivity(), "" + name + " " + position, Toast.LENGTH_SHORT).show(); } @Override public void onPaidCheck(String name, int position) { Toast.makeText(getActivity(), "" + name + " " + position, Toast.LENGTH_SHORT).show(); } @Override public void onPaidUncheck(String name, int position) { Toast.makeText(getActivity(), "" + name + " " + position, Toast.LENGTH_SHORT).show(); } }); 

la clase pública TagYourDiseaseAdapter amplía RecyclerView.Adapter {private ReCyclerViewItemClickListener mRecyclerViewItemClickListener; Contexto privado mContext;

 List deviceList = Collections.emptyList(); /** * Initialize the values * * @param context : context reference * @param devices : data */ public TagYourDiseaseAdapter(Context context, List devices, ReCyclerViewItemClickListener mreCyclerViewItemClickListener) { this.mContext = context; this.deviceList = devices; this.mRecyclerViewItemClickListener = mreCyclerViewItemClickListener; } /** * @param parent : parent ViewPgroup * @param viewType : viewType * @return ViewHolder * 

* Inflate the Views * Create the each views and Hold for Reuse */ @Override public TagYourDiseaseAdapter.OrderHistoryViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_tag_disease, parent, false); TagYourDiseaseAdapter.OrderHistoryViewHolder myViewHolder = new TagYourDiseaseAdapter.OrderHistoryViewHolder(view); return myViewHolder; } /** * @param holder :view Holder * @param position : position of each Row * set the values to the views */ @Override public void onBindViewHolder(final TagYourDiseaseAdapter.OrderHistoryViewHolder holder, final int position) { Picasso.with(mContext).load(deviceList.get(position).getIconUrl()).into(holder.document); holder.name.setText(deviceList.get(position).getDiseaseName()); holder.radioButton.setOnCheckedChangeListener(null); holder.radioButton.setChecked(deviceList.get(position).isChecked()); //if true, your checkbox will be selected, else unselected //holder.radioButton.setChecked(objIncome.isSelected()); holder.radioButton.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { deviceList.get(position).setChecked(isChecked); } }); } @Override public int getItemCount() { return deviceList.size(); } /** * Create The view First Time and hold for reuse * View Holder for Create and Hold the view for ReUse the views instead of create again * Initialize the views */ public class OrderHistoryViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { ImageView document; TextView name; CheckBox radioButton; public OrderHistoryViewHolder(View itemView) { super(itemView); document = itemView.findViewById(R.id.img_tag); name = itemView.findViewById(R.id.text_tag_name); radioButton = itemView.findViewById(R.id.rdBtn_tag_disease); radioButton.setOnClickListener(this); //this.setIsRecyclable(false); } @Override public void onClick(View view) { mRecyclerViewItemClickListener.onItemClickListener(this.getAdapterPosition(), view); } }

}

Usando Kotlin, lo único que resolvió este problema fue borrar el OnCheckedChangeListener antes de establecer la variable y luego crear un nuevo OnCheckedChangeListener después de que se haya configurado.

Hago lo siguiente en mi RecyclerView.ViewHolder

 task.setOnCheckedChangeListener(null) task.isChecked = item.status task.setOnCheckedChangeListener { _: CompoundButton, checked: Boolean -> item.status = checked ... do more stuff ... } 

Recomiendo que no use checkBox.setOnCheckedChangeListener en recyclerViewAdapter . Porque en el desplazamiento de recyclerView, checkBox.setOnCheckedChangeListener será disparado por el adaptador. No es seguro . En su lugar, use checkBox.setOnClickListener para interactuar con las entradas del usuario.

Por ejemplo:

  public void onBindViewHolder(final ViewHolder holder, int position) { /* . . . . . . */ holder.checkBoxAdapterTasks.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { boolean isChecked = holder.checkBoxAdapterTasks.isChecked(); if(isChecked){ //checkBox clicked and checked }else{ //checkBox clicked and unchecked } } }); } 

Lo que funcionó para mí es anular a los oyentes en el viewHolder cuando la vista se va a reciclar (en onViewRecycled ):

  override fun onViewRecycled(holder: AttendeeViewHolder) { super.onViewRecycled(holder) holder.itemView.hasArrived.setOnCheckedChangeListener(null); holder.itemView.edit.setOnClickListener { null } }