studio recyclerview make how dependency create android android-recyclerview

android - make - recyclerview dependency



Android RecyclerView: notifyDataSetChanged() IllegalStateException (20)

Estoy tratando de actualizar los elementos de una vista de reciclaje usando notifyDataSetChanged ().

Este es mi método onBindViewHolder () en el adaptador de reciclaje.

@Override public void onBindViewHolder(ViewHolder viewHolder, int position) { //checkbox view listener viewHolder.getCheckbox().setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { //update list items notifyDataSetChanged(); } }); }

Lo que quiero hacer es actualizar los elementos de la lista, después de marcar una casilla de verificación. Sin embargo, recibo una excepción ilegal: "Cannot call this method while RecyclerView is computing a layout or scrolling"

java.lang.IllegalStateException: Cannot call this method while RecyclerView is computing a layout or scrolling at android.support.v7.widget.RecyclerView.assertNotInLayoutOrScroll(RecyclerView.java:1462) at android.support.v7.widget.RecyclerView$RecyclerViewDataObserver.onChanged(RecyclerView.java:2982) at android.support.v7.widget.RecyclerView$AdapterDataObservable.notifyChanged(RecyclerView.java:7493) at android.support.v7.widget.RecyclerView$Adapter.notifyDataSetChanged(RecyclerView.java:4338) at com.app.myapp.screens.RecycleAdapter.onRowSelect(RecycleAdapter.java:111)

También usé notifyItemChanged (), la misma excepción. ¿Alguna forma secreta de actualizar para notificar al adaptador que algo cambió?


¿Por qué no comprobar el estado RecyclerView.isComputingLayout() la siguiente manera?

public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{ private RecyclerView mRecyclerView; @Override public void onAttachedToRecyclerView(RecyclerView recyclerView) { super.onAttachedToRecyclerView(recyclerView); mRecyclerView = recyclerView; } @Override public void onBindViewHolder(ViewHolder viewHolder, int position) { viewHolder.getCheckbox().setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { if (mRecyclerView != null && !mRecyclerView.isComputingLayout()) { notifyDataSetChanged(); } } }); } }


Al principio pensé que la respuesta de Moonsoo (la respuesta aceptada) no funcionaría para mí porque no puedo inicializar mi setOnCheckedChangeListener() en el constructor ViewHolder porque necesito vincularlo cada vez para obtener una variable de posición actualizada. Pero me llevó mucho tiempo darme cuenta de lo que estaba diciendo.

Aquí hay un ejemplo de la "llamada al método circular" de la que está hablando:

public void onBindViewHolder(final ViewHolder holder, final int position) { SwitchCompat mySwitch = (SwitchCompat) view.findViewById(R.id.switch); mySwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { if (isChecked) { data.delete(position); notifyItemRemoved(position); //This will call onBindViewHolder, but we can''t do that when we are already in onBindViewHolder! notifyItemRangeChanged(position, data.size()); } } }); //Set the switch to how it previously was. mySwitch.setChecked(savedSwitchState); //If the saved state was "true", then this will trigger the infinite loop. }

El único problema con esto es que cuando necesitamos inicializar el interruptor para que se onBindViewHolder o desactive (desde el estado guardado en el pasado, por ejemplo), está llamando al oyente, que podría llamar a nofityItemRangeChanged que onBindViewHolder llamar a onBindViewHolder . No puede llamar a onBindViewHolder cuando ya está en onBindViewHolder ], porque no puede notifyItemRangeChanged si ya está en medio de la notificación de que el rango de elementos ha cambiado. Pero solo necesitaba actualizar la interfaz de usuario para mostrarla activada o desactivada, no queriendo realmente desencadenar nada.

Aquí está la solución que aprendí de la respuesta de JoniDS que evitará el ciclo infinito. Siempre que configuremos el oyente como "nulo" antes de configurar Checked, actualizará la IU sin activar el oyente, evitando el bucle infinito. Entonces podemos establecer el oyente después.

Código de JoniDS:

holder.checkbox.setOnCheckedChangeListener(null); holder.checkbox.setChecked(condition); holder.checkbox.setOnCheckedChangeListener(checkedListener);

Solución completa a mi ejemplo:

public void onBindViewHolder(final ViewHolder holder, final int position) { SwitchCompat mySwitch = (SwitchCompat) view.findViewById(R.id.switch); //Set it to null to erase an existing listener from a recycled view. mySwitch.setOnCheckedChangeListener(null); //Set the switch to how it previously was without triggering the listener. mySwitch.setChecked(savedSwitchState); //If the saved state was "true", then this will trigger the infinite loop. //Set the listener now. mySwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { if (isChecked) { data.delete(position); notifyItemRemoved(position); //This will call onBindViewHolder, but we can''t do that when we are already in onBindViewHolder! notifyItemRangeChanged(position, data.size()); } } }); }


Antes notifyDataSetChanged() acaba de comprobar que con este método: recyclerView.IsComputingLayout()


Cuando tienes el mensaje de error:

Cannot call this method while RecyclerView is computing a layout or scrolling

Simple, solo haz lo que causa la excepción en:

RecyclerView.post(new Runnable() { @Override public void run() { /** ** Put Your Code here, exemple: **/ notifyItemChanged(position); } });


Debe moverse método ''setOnCheckedChangeListener ()'' a ViewHolder que es clase interna en el adaptador.

onBindViewHolder() no es un método que inicializa ViewHolder . Este método es el paso de actualizar cada elemento de reciclaje. Cuando se llama a notifyDataSetChanged() , onBindViewHolder() se llama como el número de veces cada elemento.

Entonces, si notifyDataSetChanged() en onCheckChanged() e inicializa checkBox en onBindViewHolder() , obtendrá IllegalStateException debido a la llamada al método circular.

haga clic en la casilla de verificación -> onCheckedChanged () -> notifyDataSetChanged () -> onBindViewHolder () -> establezca la casilla de verificación -> onChecked ...

Simplemente, puede solucionar esto colocando una bandera en Adaptador.

prueba esto,

private boolean onBind; public ViewHolder(View itemView) { super(itemView); mCheckBox = (CheckBox) itemView.findViewById(R.id.checkboxId); mCheckBox.setOnCheckChangeListener(this); } @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { if(!onBind) { // your process when checkBox changed // ... notifyDataSetChanged(); } } ... @Override public void onBindViewHolder(YourAdapter.ViewHolder viewHolder, int position) { // process other views // ... onBind = true; viewHolder.mCheckBox.setChecked(trueOrFalse); onBind = false; }


El uso de un Handler para agregar elementos y llamar a notify...() de este Handler solucionó el problema por mí.


Encontró una solución simple:

public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{ private RecyclerView mRecyclerView; @Override public void onAttachedToRecyclerView(RecyclerView recyclerView) { super.onAttachedToRecyclerView(recyclerView); mRecyclerView = recyclerView; } private CompoundButton.OnCheckedChangeListener checkedChangeListener = (compoundButton, b) -> { final int position = (int) compoundButton.getTag(); // This class is used to make changes to child view final Event event = mDataset.get(position); // Update state of checkbox or some other computation which you require event.state = b; // we create a runnable and then notify item changed at position, this fix crash mRecyclerView.post(new Runnable() { @Override public void run() { notifyItemChanged(position)); } }); } }

Aquí creamos un ejecutable para notificar a ItemChanged para una posición cuando la vista de reciclaje está lista para manejarla.


Esto sucede porque probablemente esté configurando el ''oyente'' antes de configurar el valor para esa fila, lo que hace que el oyente se active cuando ''configura el valor'' para la casilla de verificación.

Lo que debes hacer es:

@Override public void onBindViewHolder(YourAdapter.ViewHolder viewHolder, int position) { viewHolder.mCheckBox.setOnCheckedChangeListener(null); viewHolder.mCheckBox.setChecked(trueOrFalse); viewHolder.setOnCheckedChangeListener(yourCheckedChangeListener); }


Me encontré con este problema exacto! Después de que la respuesta de Moonsoo realmente no hizo flotar mi bote, me equivoqué un poco y encontré una solución que funcionó para mí.

En primer lugar, aquí es parte de mi código:

@Override public void onBindViewHolder(ViewHolder holder, final int position) { final Event event = mDataset.get(position); // // ....... // holder.mSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { event.setActive(isChecked); try { notifyItemChanged(position); } catch (Exception e) { Log.e("onCheckChanged", e.getMessage()); } } });

Notarás que estoy notificando específicamente al adaptador la posición que estoy cambiando, en lugar de todo el conjunto de datos como lo estás haciendo. Dicho esto, aunque no puedo garantizar que esto funcione para usted, resolví el problema envolviendo mi llamada notifyItemChanged() en un bloque try / catch. ¡Esto simplemente captó la excepción, pero aún permitió que mi adaptador registrara el cambio de estado y actualizara la pantalla!

¡Espero que esto ayude a alguien!

EDITAR: Admito que probablemente esta no sea la forma adecuada / madura de manejar el problema, pero dado que no parece estar causando ningún problema al dejar la excepción sin controlar, pensé en compartir en caso de que fuera bueno suficiente para alguien más.


Mientras el administrador de diseño vincula el elemento, es muy probable que esté configurando el estado marcado de su casilla de verificación, que está activando la devolución de llamada.

Por supuesto, esto es una suposición porque no publicó el seguimiento completo de la pila.

No puede cambiar el contenido del adaptador mientras RV está recalculando el diseño. Puede evitarlo no llamando a notifyDataSetChanged si el estado marcado del elemento es igual al valor enviado en la devolución de llamada (que será el caso si llamar a checkbox.setChecked está activando la devolución de llamada).


No sé bien, pero también tenía el mismo problema. onClickListner esto usando onClickListner en la checkbox de checkbox

viewHolder.mCheckBox.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub if (model.isCheckboxBoolean()) { model.setCheckboxBoolean(false); viewHolder.mCheckBox.setChecked(false); } else { model.setCheckboxBoolean(true); viewHolder.mCheckBox.setChecked(true); } notifyDataSetChanged(); } });

¡Prueba esto, esto puede ayudar!


Para mí, el problema ocurrió cuando salí de EditText por Listo, Atrás o toque de entrada externo. Esto hace que se actualice el modelo con el texto de entrada, luego se actualiza la vista del reciclador mediante la observación de datos en vivo.

El problema era que el cursor / foco permanecía en EditText.

Cuando he eliminado enfoque mediante el uso de:

editText.clearFocus()

El método de notificación de cambio de datos de la vista del reciclador dejó de arrojar este error.

Creo que esta es una de las posibles razones / soluciones a este problema. Es posible que esta excepción se pueda solucionar de otra manera, ya que puede ser causada por una razón totalmente diferente.


Publicación de uso simple:

new Handler().post(new Runnable() { @Override public void run() { mAdapter.notifyItemChanged(mAdapter.getItemCount() - 1); } } });


Puede reiniciar el escucha anterior antes de realizar cambios y no obtendrá esta excepción.

private CompoundButton.OnCheckedChangeListener checkedListener = new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { //Do your stuff });; @Override public void onBindViewHolder(final ViewHolder holder, final int position) { holder.checkbox.setOnCheckedChangeListener(null); holder.checkbox.setChecked(condition); holder.checkbox.setOnCheckedChangeListener(checkedListener); }


Use onClickListner en la casilla de verificación en lugar de OnCheckedChangeListener, resolverá el problema

viewHolder.myCheckBox.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if (viewHolder.myCheckBox.isChecked()) { // Do something when checkbox is checked } else { // Do something when checkbox is unchecked } notifyDataSetChanged(); } });


Yo tenía el mismo problema con la casilla de verificación y RadioButton. Sustituir notifyDataSetChanged() con notifyItemChanged(position) funcionó. isChecked un campo booleano isChecked al modelo de datos. A continuación, he actualizado el valor booleano y en onCheckedChangedListener , llamé notifyItemChanged(adapterPosition) . Esto podría no ser la mejor manera, pero funcionó para mí. El valor booleano se usa para verificar si el elemento está marcado.


simplemente utilizar isPressed() método de CompoundButton en onCheckedChanged(CompoundButton compoundButton, boolean isChecked)
p.ej

public void onCheckedChanged(CompoundButton compoundButton, boolean isChecked) { ... //your functionality if(compoundButton.isPressed()){ notifyDataSetChanged(); } } });


su elemento CheckBox está en cambio dibujable cuando llama a notifyDataSetChanged(); entonces esta excepción ocurriría. Intente llamar a notifyDataSetChanged(); en post de su vista. Por ejemplo:

buttonView.post(new Runnable() { @Override public void run() { notifyDataSetChanged(); } });


protected void postAndNotifyAdapter(final Handler handler, final RecyclerView recyclerView, final RecyclerView.Adapter adapter) { handler.post(new Runnable() { @Override public void run() { if (!recyclerView.isComputingLayout()) { adapter.notifyDataSetChanged(); } else { postAndNotifyAdapter(handler, recyclerView, adapter); } } }); }


@Override public void onBindViewHolder(final MyViewHolder holder, final int position) { holder.textStudentName.setText(getStudentList.get(position).getName()); holder.rbSelect.setChecked(getStudentList.get(position).isSelected()); holder.rbSelect.setTag(position); // This line is important. holder.rbSelect.setOnClickListener(onStateChangedListener(holder.rbSelect, position)); } @Override public int getItemCount() { return getStudentList.size(); } private View.OnClickListener onStateChangedListener(final RadioButton checkBox, final int position) { return new View.OnClickListener() { @Override public void onClick(View v) { if (checkBox.isChecked()) { for (int i = 0; i < getStudentList.size(); i++) { getStudentList.get(i).setSelected(false); } getStudentList.get(position).setSelected(checkBox.isChecked()); notifyDataSetChanged(); } else { } } }; }