recyclerview item invalid inconsistency detected android android-layout android-recyclerview screen-rotation

android - item - RecyclerView: inconsistencia detectada. Posición inválida del artículo



inconsistency detected invalid item position recyclerview (30)

En mi caso (borrar / insertar datos en mi estructura de datos) ¡necesitaba borrar el grupo de reciclaje y luego notificar que el conjunto de datos cambió!

mRecyclerView.getRecycledViewPool().clear(); mAdapter.notifyDataSetChanged();

Nuestro control de calidad ha detectado un error: al girar el dispositivo Android (Droid Turbo), se produjo el siguiente bloqueo relacionado con RecyclerView :

java.lang.IndexOutOfBoundsException: Inconsistencia detectada. Posición de elemento no válida 2 (desplazamiento: 2). Estado: 3

Para mí, parece un error interno dentro de RecyclerView, ya que no puedo pensar en ninguna forma de que esto sea causado directamente por nuestro código ...

¿Alguien ha encontrado este problema?

¿Cuál sería la solución?

Una solución brutal podría ser quizás detectar la excepción cuando sucede y volver a crear la instancia de RecyclverView desde cero, para evitar quedarse con un estado corrupto.

Pero, si es posible, me gustaría entender mejor el problema (y quizás solucionarlo en su origen), en lugar de enmascararlo.

El error no es fácil de reproducir, pero es fatal cuando ocurre.

El seguimiento completo de la pila:

W/dalvikvm( 7546): threadid=1: thread exiting with uncaught exception (group=0x41987d40) E/AndroidRuntime( 7546): FATAL EXCEPTION: main E/AndroidRuntime( 7546): Process: com.oblong.mezzedroid, PID: 7546 E/AndroidRuntime( 7546): java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid item position 2(offset:2).state:3 E/AndroidRuntime( 7546): at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:3382) E/AndroidRuntime( 7546): at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:3340) E/AndroidRuntime( 7546): at android.support.v7.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:1810) E/AndroidRuntime( 7546): at android.support.v7.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1306) E/AndroidRuntime( 7546): at android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1269) E/AndroidRuntime( 7546): at android.support.v7.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:523) E/AndroidRuntime( 7546): at org.liboid.recycler_view.RecyclerViewContainer$LiLinearLayoutManager.onLayoutChildren(RecyclerViewContainer.java:179) E/AndroidRuntime( 7546): at android.support.v7.widget.RecyclerView.dispatchLayout(RecyclerView.java:1942) E/AndroidRuntime( 7546): at android.support.v7.widget.RecyclerView.onLayout(RecyclerView.java:2237) E/AndroidRuntime( 7546): at org.liboid.recycler_view.LiRecyclerView.onLayout(LiRecyclerView.java:30) E/AndroidRuntime( 7546): at android.view.View.layout(View.java:14946) E/AndroidRuntime( 7546): at android.view.ViewGroup.layout(ViewGroup.java:4651) E/AndroidRuntime( 7546): at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453) E/AndroidRuntime( 7546): at android.widget.FrameLayout.onLayout(FrameLayout.java:388) E/AndroidRuntime( 7546): at android.view.View.layout(View.java:14946) E/AndroidRuntime( 7546): at android.view.ViewGroup.layout(ViewGroup.java:4651) E/AndroidRuntime( 7546): at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453) E/AndroidRuntime( 7546): at android.widget.FrameLayout.onLayout(FrameLayout.java:388) E/AndroidRuntime( 7546): at android.view.View.layout(View.java:14946) E/AndroidRuntime( 7546): at android.view.ViewGroup.layout(ViewGroup.java:4651) E/AndroidRuntime( 7546): at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1671) E/AndroidRuntime( 7546): at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1525) E/AndroidRuntime( 7546): at android.widget.LinearLayout.onLayout(LinearLayout.java:1434) E/AndroidRuntime( 7546): at com.oblong.mezzedroid.workspace.content.bins.BinsContainerLayout.onLayout(BinsContainerLayout.java:22) E/AndroidRuntime( 7546): at android.view.View.layout(View.java:14946) E/AndroidRuntime( 7546): at android.view.ViewGroup.layout(ViewGroup.java:4651) E/AndroidRuntime( 7546): at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1671) E/AndroidRuntime( 7546): at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1525) E/AndroidRuntime( 7546): at android.widget.LinearLayout.onLayout(LinearLayout.java:1434) E/AndroidRuntime( 7546): at android.view.View.layout(View.java:14946) E/AndroidRuntime( 7546): at android.view.ViewGroup.layout(ViewGroup.java:4651) E/AndroidRuntime( 7546): at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453) E/AndroidRuntime( 7546): at android.widget.FrameLayout.onLayout(FrameLayout.java:388) E/AndroidRuntime( 7546): at android.view.View.layout(View.java:14946) E/AndroidRuntime( 7546): at android.view.ViewGroup.layout(ViewGroup.java:4651) E/AndroidRuntime( 7546): at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453) E/AndroidRuntime( 7546): at android.widget.FrameLayout.onLayout(FrameLayout.java:388) E/AndroidRuntime( 7546): at android.view.View.layout(View.java:14946) E/AndroidRuntime( 7546): at android.view.ViewGroup.layout(ViewGroup.java:4651) E/AndroidRuntime( 7546): at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1671) E/AndroidRuntime( 7546): at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1525) E/AndroidRuntime( 7546): at android.widget.LinearLayout.onLayout(LinearLayout.java:1434) E/AndroidRuntime( 7546): at android.view.View.layout(View.java:14946) E/AndroidRuntime( 7546): at android.view.ViewGroup.layout(ViewGroup.java:4651) E/AndroidRuntime( 7546): at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453) E/AndroidRuntime( 7546): at android.widget.FrameLayout.onLayout(FrameLayout.java:388) E/AndroidRuntime( 7546): at android.view.View.layout(View.java:14946) E/AndroidRuntime( 7546): at android.view.ViewGroup.layout(ViewGroup.java:4651) E/AndroidRuntime( 7546): at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1671) E/AndroidRuntime( 7546): at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1525) E/AndroidRuntime( 7546): at android.widget.LinearLayout.onLayout(LinearLayout.java:1434) E/AndroidRuntime( 7546): at android.view.View.layout(View.java:14946) E/AndroidRuntime( 7546): at android.view.ViewGroup.layout(ViewGroup.java:4651) E/AndroidRuntime( 7546): at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453) E/AndroidRuntime( 7546): at android.widget.FrameLayout.onLayout(FrameLayout.java:388) E/AndroidRuntime( 7546): at android.view.View.layout(View.java:14946) E/AndroidRuntime( 7546): at android.view.ViewGroup.layout(ViewGroup.java:4651) E/AndroidRuntime( 7546): at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:2132) E/AndroidRuntime( 7546): at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1872) E/AndroidRuntime( 7546): at andro


En mi caso, acabo de eliminar la línea con setHasStableIds(true);


En mi caso, estaba actualizando los elementos y llamando a notifyDataSetChanged en un hilo sin interfaz de usuario. La mayoría de las veces funcionó, pero cuando ocurrieron muchos cambios rápidamente, colapsó. Cuando lo hice, en cambio, básicamente

activity.runOnUiThread(new Runnable() { @Override public void run() { changeData(); notifyDataSetChanged(); } });

entonces dejó de estrellarse.


En mi caso, estaba tratando de cambiar el contenido de mi adaptador en un subproceso de fondo, pero llamé a notify * en el subproceso principal / ui.

¡Eso no es posible! La razón por la que forzar la notificación al hilo principal es que la vista de reciclador quiere que edite su adaptador de respaldo en el hilo principal, incluso en la misma pila de llamadas.

Para resolver el problema, asegúrese de que cada operación a su adaptador, así como cada notificación ... ¡la llamada se realice en el subproceso ui / main !


Encontré esa configuración mRecycler.setLayoutFrozen (verdadero); en el método onRefresh del swipeContainer.

Resuelto el problema para mí.

swipeContainer.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { @Override public void onRefresh() { orderlistRecycler.setLayoutFrozen(true); loadData(false); } });


Este error ocurre cuando la lista en el adaptador se borra cuando el usuario se desplaza, lo que hace que la posición del titular del elemento cambie, la referencia perdida entre la lista y el elemento en la interfaz de usuario, el error ocurre en la próxima solicitud "notifyDataSetChanged"

Fijar:

Revise su método de lista de actualizaciones. Si haces algo como

mainList.clear(); ... mainList.add() or mainList.addAll() ... notifyDataSetChanged(); ===> Error occur

Como arreglar. Cree un nuevo objeto de lista para el procesamiento del búfer y luego vuelva a asignarlo a la lista principal

List res = new ArrayList(); ….. res.add(); //add item or modify list …. mainList = res; notifyDataSetChanged();

Gracias a Nhan Cao por esta gran ayuda :)


Este es un error bastante desagradable.

Para manejar el clic de mi elemento, utilicé una implementación de RecyclerView.OnItemTouchListener similar a la solución encontrada en esta pregunta .

Después de actualizar muchas veces la RecyclerView de datos del RecyclerView y hacer clic en un elemento, esta IndexOutOfBoundsException podría bloquear mi aplicación. Cuando se hace clic en un elemento, RecyclerView busca internamente la vista subyacente correcta y le devuelve la posición. Al revisar el código fuente, vi que había algunas Tasks e Threads programados. Para abreviar la historia, básicamente es solo un estado ilegal donde dos fuentes de datos están entremezcladas y no sincronizadas y todo se vuelve loco.

En base a esto, ViewHolder mi implementación de RecyclerView.OnItemTouchListener y simplemente ViewHolder el clic en ViewHolder del Adapter yo mismo:

public void onBindViewHolder (final BaseContentView holder, final int position) { holder.itemView.setOnClickListener(new OnClickListener() { @Override public void onClick (View view) { // do whatever you like here } }); }

Puede que esta no sea la mejor solución, pero está libre de bloqueos por ahora. Espero que esto le ahorre algo de tiempo :).


Estoy alterando los datos para RecyclerView en el Thread fondo. Tengo la misma Exception que el OP. Agregué esto después de cambiar los datos:

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

Espero eso ayude


Lint me dio un consejo sobre la inconsistencia: escribí (onBindViewHolder ()):

pholder.mRlayout.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { doStuff(position); } });

que tuvo que ser reemplazado por:

pholder.mRlayout.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { doStuff(pholder.getAdapterPosition()); } });

¡Ejecute ambos códigos en su código y luego ejecute Lint para la explicación completa!


Lo sentimos por la solución tardía pero perfecta: cuando intente eliminar un elemento específico, simplemente llame a notifydatasetchange () y obtenga este elemento en bindviewholder y elimine ese elemento y agregue nuevamente al último de la lista y luego revise la posición de la lista si este es el último índice y luego elimine ít. Básicamente, el problema surge cuando intenta eliminar el elemento del centro. si elimina el elemento del último índice, entonces no habrá más reciclaje y su recuento de adpter será mantina (este es un punto crítico de caída, venga aquí) y la falla se resuelve en el fragmento de código a continuación.

holder.itemLayout.setVisibility( View.GONE );//to hide temprory it show like you have removed item Model current = list.get( position ); list.remove( current ); list.add( list.size(), current );//add agine to last index if(position==list.size()-1){// remove from last index list.remove( position ); }


Me encontré con este desagradable rastro de pila con los nuevos componentes de arquitectura de Android recientemente. Esencialmente, tengo una lista de elementos en mi ViewModel que mi Fragmento observa, usando LiveData. Cuando ViewModel publica un nuevo valor para los datos, Fragment actualiza el adaptador, pasa estos nuevos elementos de datos y notifica al adaptador que ha habido cambios.

¡Desafortunadamente, al pasar los nuevos elementos de datos al adaptador, no tuve en cuenta el hecho de que tanto el ViewModel como el Adaptador estarían apuntando a la misma referencia de objeto! Lo que significa que si actualizo los datos y llamo a postValue() desde ViewModel, ¡hay una ventana muy pequeña donde los datos podrían actualizarse y el adaptador aún no ha sido notificado!

Mi solución fue crear una instancia de una copia nueva de los elementos cuando se pasa al adaptador:

mList = new ArrayList<>(passedList);

Con esta solución súper fácil, puede estar seguro de que los datos de su adaptador no cambiarán hasta justo antes de que se notifique a su adaptador.


Me encontré con un problema similar y lo resolví. Codifiqué algunos ejemplos para un caso de prueba, pero no me aseguré de que cada uno devolviera una identificación única y eso me causó el siguiente bloqueo. La solución de los ID resolvió el problema, ¡espero que esto ayude a alguien más!


Me he enfrentado a la misma situación. Y se resolvió agregando códigos antes de borrar su colección.

mRecyclerView.getRecycledViewPool().clear();


Mi problema desapareció después de modificar la implementación de mi Adapter para usar una copia de la matriz de elementos en lugar de una referencia. Se setItems() método setItems() cada vez que tenemos nuevos elementos para mostrar en RecyclerView .

En vez de:

private class MyAdapter extends RecyclerView.Adapter<ItemHolder> { private List<MyItem> mItems; (....) void setItems(List<MyItem> items) { mItems = items; } }

Yo si:

void setItems(List<MyItem> items) { mItems = new ArrayList<>(items); }


Para solucionar este problema, solo llame a notifyDataSetChanged () con una lista vacía antes de actualizar la vista de reciclaje.

Por ejemplo

//Method for refresh recycle view if (!hcpArray.isEmpty())

hcpArray.clear (); // La lista para la vista de reciclaje de actualización

adapter.notifyDataSetChanged();


Resolví el problema, agregando elementos uno por uno cuando se obtienen nuevos datos. Yo uso esta función dentro del adaptador.

public void add(Data item) { if(!params.contains(item){ params.add(item); notifyItemInserted(getItemCount() - 1); } } }


Simplemente elimine todas las vistas de su Administrador de diseño antes de notificar. me gusta:

myLayoutmanager.removeAllViews();


Solo necesita borrar su lista en OnPostExecute() y no mientras hace Pull to Refresh

// Setup refresh listener which triggers new data loading swipeContainer.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { @Override public void onRefresh() { AsyncTask<String,Void,String> task = new get_listings(); task.execute(); // clear listing inside onPostExecute } });

Descubrí que esto sucede cuando se desplaza durante una extracción para actualizar , ya que estaba borrando la lista antes de la async task , lo que resultó en java.lang.IndexOutOfBoundsException: Inconsistency detected.

swipeContainer.setRefreshing(false); //TODO : This is very crucial , You need to clear before populating new items listings.clear();

De esa manera no terminarás con una inconsistencia


También puede estar relacionado con la configuración del adaptador varias veces al mismo tiempo. Tenía un método de devolución de llamada que se activó 5-6 veces al mismo tiempo y estaba configurando el adaptador en esa devolución de llamada para que RecycledViewPool no pudiera manejar todos esos datos al mismo tiempo. Es una gran oportunidad, pero es mejor que lo compruebes de todos modos.


Tengo el mismo problema. Ocurrió cuando me desplazaba rápido y llamaba a API y actualizaba los datos. Después de intentar todo para evitar el bloqueo, encontré la solución.

mRecyclerView.stopScroll();

Funcionará.


Tuve el mismo problema con recyclerView, así que acabo de notificar al adaptador sobre el cambio del conjunto de datos justo después de que se borre la lista.

mList.clear(); mAdapter.notifyDataSetChanged(); mList.addAll(newData); mAdapter.notifyDataSetChanged();


Tuve un mismo problema anteriormente. Finalmente encontré una solución para eso

Lo que hago es notificar al adaptador que el elemento se ha eliminado y luego notificar que el rango del conjunto de datos del adaptador ha cambiado

public void setData(List<Data> dataList) { if (this.dataList.size() > 0) { notifyItemRangeRemoved(0, dataList.size()); this.dataList.clear(); } this.dataList.addAll(dataList) notifyItemRangeChanged(0, dataList.size()); }


Tuve un problema (posiblemente) relacionado: ingresar una nueva instancia de una actividad con un RecyclerView, pero con un adaptador más pequeño estaba desencadenando este bloqueo para mí.

RecyclerView.dispatchLayout() puede intentar extraer elementos del desecho antes de llamar a mRecycler.clearOldPositions() . La consecuencia es que extraía elementos del grupo común que tenían posiciones más altas que el tamaño del adaptador.

Afortunadamente, solo hace esto si PredictiveAnimations está habilitado, por lo que mi solución fue subclasificar GridLayoutManager ( LinearLayoutManager tiene el mismo problema y ''corregir''), y anular supportsPredictiveItemAnimations() para devolver falso:

/** * No Predictive Animations GridLayoutManager */ private static class NpaGridLayoutManager extends GridLayoutManager { /** * Disable predictive animations. There is a bug in RecyclerView which causes views that * are being reloaded to pull invalid ViewHolders from the internal recycler stack if the * adapter size has decreased since the ViewHolder was recycled. */ @Override public boolean supportsPredictiveItemAnimations() { return false; } public NpaGridLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } public NpaGridLayoutManager(Context context, int spanCount) { super(context, spanCount); } public NpaGridLayoutManager(Context context, int spanCount, int orientation, boolean reverseLayout) { super(context, spanCount, orientation, reverseLayout); } }


Tuve un problema similar pero no exactamente el mismo. En mi caso, en 1 punto estaba borrando la matriz que se pasó a la vista del reciclador

mObjects.clear();

y no llamar a notifyDataSetChanged, ya que no quería que la vista del reciclador borrara inmediatamente las vistas. Estaba rellenando la matriz de mObjects en AsyncTask.


Una vez recibí el error también:

Causa: estaba tratando de actualizar una vista de reciclador desde una tarea asíncrona mientras intentaba simultáneamente obtener antiguos viewHolders borrados;

Código: Genero datos con solo presionar un botón, lógica de la siguiente manera

  1. Borrar los últimos elementos en la vista de reciclador
  2. Llame a la tarea asíncrona para generar datos
  3. OnPostExecute Actualiza la vista del Reciclador y NotifyDataSetChanged

Problema: cada vez que me desplazo rápido antes de generar mis datos, obtengo

Inconsistencia detectada. Adaptador de soporte de vista no válido positionViewHolder java.lang.IndexOutOfBoundsException: Inconsistencia detectada. Posición de elemento no válida 20 (desplazamiento: 2). Estado: 3

Solución: en lugar de borrar el RecyclerView antes de generar mis datos, lo dejo y luego lo Reemplace con los Nuevos Datos, el Call NotifyDatasetChanged, como se muestra a continuación;

@Override protected void onPostExecute(List<Objects> o) { super.onPostExecute(o); recyclerViewAdapter.setList(o); mProgressBar.setVisibility(View.GONE); mRecyclerView.setVisibility(View.VISIBLE); }


Utilice notifyDataSetChanged() en notifyItem... lugar notifyItem... en este caso.


Utilizar

notifyDataSetChanged()

en lugar

notifyItemRangeInserted(0, YourArrayList.size())

en este caso.


add_location.removeAllViews ();

for (int i=0;i<arrayList.size();i++) { add_location.addView(new HolderDropoff(AddDropOffActivtity.this,add_location,arrayList,AddDropOffActivtity.this,this)); } add_location.getAdapter().notifyDataSetChanged();


este problema puede ocurrir cuando intentas borrar tu lista, si vas a borrar tu lista de datos, especialmente cuando usas pull para actualizar, intenta usar un indicador booleano, inicialízalo como falso y dentro del método OnRefresh hazlo verdadero, borra tu dataList si el indicador es verdadero justo antes de agregarle los nuevos datos y luego hacerlo falso.

tu código podría ser así

private boolean pullToRefreshFlag = false ; private ArrayList<your object> dataList ; private Adapter adapter ; public class myClass extend Fragment implements SwipeRefreshLayout.OnRefreshListener{ private void requestUpdateList() { if (pullToRefresh) { dataList.clear pullToRefreshFlag = false; } dataList.addAll(your data); adapter.notifyDataSetChanged; @Override OnRefresh() { PullToRefreshFlag = true reqUpdateList() ; } }


mRecycler.setAdapter(itemsAdapter) esto retrasando el mRecycler.setAdapter(itemsAdapter) hasta después de agregar todos los artículos al adaptador con mRecycler.addAll(items) y funcionó. No tengo idea de por qué lo hice para empezar, fue desde el código de una biblioteca que miré y vi esas líneas en el "orden incorrecto", estoy bastante seguro de que es así, por favor, si alguien puede confirmarlo, explique por qué ¿asi que? No estoy seguro si esta es una respuesta válida incluso