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
- Borrar los últimos elementos en la vista de reciclador
- Llame a la tarea asíncrona para generar datos
- 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