Inconsistencia detectada en RecyclerView, Cómo cambiar el contenido de RecyclerView mientras se desplaza

Estoy usando RecyclerView para mostrar el nombre de los elementos. Mi fila contiene solo TextView . Los nombres de elementos se almacenan en List<String> mItemList .

Para cambiar el contenido de RecyclerView , reemplazo las cadenas en mItemList y llamo a notifyDataSetChanged () en RecyclerViewAdapter .

Pero si intento cambiar el contenido de mItemList mientras RecyclerView está desplazándose, a veces me da java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid item position 157(offset:157).state:588 java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid item position 157(offset:157).state:588

Esto sucede si el tamaño de mItemList es menor que antes. Entonces, ¿cuál es la manera correcta de cambiar los contenidos del RecyclerView ? ¿Es esto un error en RecyclerView ?

Aquí está el rastro completo de la pila de la excepción:

 java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid item position 157(offset:157).state:588 at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:3300) at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:3258) at android.support.v7.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:1803) at android.support.v7.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1302) at android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1265) at android.support.v7.widget.LinearLayoutManager.scrollBy(LinearLayoutManager.java:1093) at android.support.v7.widget.LinearLayoutManager.scrollVerticallyBy(LinearLayoutManager.java:956) at android.support.v7.widget.RecyclerView$ViewFlinger.run(RecyclerView.java:2715) at android.view.Choreographer$CallbackRecord.run(Choreographer.java:725) at android.view.Choreographer.doCallbacks(Choreographer.java:555) at android.view.Choreographer.doFrame(Choreographer.java:524) at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:711) at android.os.Handler.handleCallback(Handler.java:615) at android.os.Handler.dispatchMessage(Handler.java:92) at android.os.Looper.loop(Looper.java:137) at android.app.ActivityThread.main(ActivityThread.java:4921) at java.lang.reflect.Method.invokeNative(Native Method) at java.lang.reflect.Method.invoke(Method.java:511) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1027) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:794) at dalvik.system.NativeStart.main(Native Method) 

Código AdapterView:

 private static class FileListAdapter extends RecyclerView.Adapter<FileHolder> { private final Context mContext; private final SparseBooleanArray mSelectedArray; private final List<String> mList; FileListAdapter(Context context, List<String> list, SparseBooleanArray selectedArray) { mList = list; mContext = context; mSelectedArray = selectedArray; } @Override public FileHolder onCreateViewHolder(ViewGroup viewGroup, int i) { View view = LayoutInflater.from(viewGroup.getContext()).inflate( R.layout.file_list_item, viewGroup, false); TextView tv = (TextView) view .findViewById(R.id.file_name_text); Typeface font = Typeface.createFromAsset(viewGroup.getContext().getAssets(), viewGroup.getContext().getString(R.string.roboto_regular)); tv.setTypeface(font); return new FileHolder(view, tv); } @Override public void onBindViewHolder(FileHolder fileHolder, final int i) { String name = mList.get(i); // highlight view if selected setSelected(fileHolder.itemView, mSelectedArray.get(i)); // Set text fileHolder.mTextView.setText(name); } @Override public int getItemCount() { return mList.size(); } } private static class FileHolder extends RecyclerView.ViewHolder { public final TextView mTextView; public FileHolder(View itemView, TextView tv) { super(itemView); mTextView = tv; } } 

Edición: el error está arreglado ahora, si todavía está recibiendo la misma excepción, asegúrese de que está actualizando su fuente de datos del adaptador sólo desde el hilo principal y llamando al adaptador apropiado notificar el método después de él.

Respuesta antigua: Parece ser un error en RecyclerView , se informa aquí y aquí . Esperemos que se arreglará en la próxima versión.

No es problema para mí. Utilice NotifyDataSetChanged ();

 public class MyFragment extends Fragment{ private MyAdapter adapter; // Your code public void addArticle(){ ArrayList<Article> list = new ArrayList<Article>(); //Add one article in this list adapter.addArticleFirst(list); // or adapter.addArticleLast(list); } } public class ArticleAdapterRecycler extends RecyclerView.Adapter<ArticleAdapterRecycler.ViewHolder> { private ArrayList<Article> Articles = new ArrayList<Article>(); private Context context; // Some functions from RecyclerView.Adapter<ArticleAdapterRecycler.ViewHolder> // Add at the top of the list. public void addArticleFirst(ArrayList<Article> list) { Articles.addAll(0, list); notifyDataSetChanged(); } // Add at the end of the list. public void addArticleLast(ArrayList<Article> list) { Articles.addAll(Articles.size(), list); notifyDataSetChanged(); } } 

Simplemente prohíba el desplazamiento de RecyclerView cuando los datos están cambiando.

Como mi código:

 mRecyclerView.setOnTouchListener( new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { if (mIsRefreshing) { return true; } else { return false; } } } ); 

Más información sobre: http://drakeet.me/recyclerview-bug-indexoutofboundsexception-inconsistency-detected-invalid-item-position-solution

Aunque un par de hipervínculos útiles acerca de este problema se dan en la respuesta aceptada, no es cierto que este comportamiento de RecyclerView mientras se desplaza es un error .

Si ve esta excepción, lo más probable es que se olvide de notificar al adaptador después de que se haya "cambiado" el contenido de RecyclerView . La gente llama a notifyDataSetChanged() sólo después de agregar un elemento al conjunto de datos. Sin embargo, la incoherencia se produce no sólo después de rellenar el adaptador, sino también cuando se quita un elemento o se borra el conjunto de datos, debe actualizar la vista notificando al adaptador acerca de este cambio:

 public void refillAdapter(Item item) { adapter.add(item); notifyDataSetChanged(); } public void cleanUpAdapter() { adapter.clear(); notifyDataSetChanged(); /* Important */ } 

En mi caso, intenté limpiar el adaptador en onStop() , y lo onStart() en onStart() . Me olvidé de llamar a notifyDataSetChanged() después de limpiar el adaptador usando clear() . Entonces, cada vez que cambié el estado de onStop() a onStart() y rápidamente desplazado el RecyclerView mientras el conjunto de datos está recargando, vi esta excepción. Si esperé el final de la recarga sin desplazamiento, no habría ninguna excepción, ya que el adaptador se puede restablecer sin problemas esta vez.

En resumen, el RecyclerView no es consistente cuando está en cambio de vista. Si intenta desplazar la vista mientras se procesan los cambios en el conjunto de datos, verá java.lang.IndexOutOfBoundsException: Inconsistency detected . Para eliminar este problema, debe notificar al adaptador inmediatamente después de cambiar el conjunto de datos.

Tuve un problema similar, pero con la eliminación de los contenidos. También quería mantener las animaciones también. Terminé usando el notifyRemove, después pasando el rango. Esto parece solucionar cualquier problema …

 public void deleteItem(int index) { try{ mDataset.remove(index); notifyItemRemoved(index); notifyItemRangeRemoved(index,1); } catch (IndexOutOfBoundsException e){ notifyDataSetChanged(); e.printStackTrace(); } } 

Parece estar trabajando y deshacerse de la excepción IOB …

Yo estaba enfrentando el mismo problema, java.lang.IndexOutOfBoundsException: Inconsistencia detectada .

Crear Custom LinearLayoutManager .

HPLinearLayoutManager.java

 public class HPLinearLayoutManager extends LinearLayoutManager { public HPLinearLayoutManager(Context context) { super(context); } public HPLinearLayoutManager(Context context, int orientation, boolean reverseLayout) { super(context, orientation, reverseLayout); } public HPLinearLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } /** * Magic here */ @Override public boolean supportsPredictiveItemAnimations() { return false; } } 

Crear instancia de HPLinearLayoutManager .

 HPLinearLayoutManager hpLinearLayoutManager = new HPLinearLayoutManager(mContext); recyclerView.setLayoutManager(hpLinearLayoutManager); 

Espero que esto te ayude.

Conseguí esto trabajar con la sugerencia de Cocorico en una respuesta anterior ( https://stackoverflow.com/a/26927186/3660638 ) pero hay una cogida: puesto que SortedList un SortedList , usando notifyDataSetChanged () cada vez que hay un cambio en datos (Agregar, quitar, etc) te hace perder las animaciones de los elementos que recibes con notifyItemXXXXX(position) , así que lo que acabé haciendo fue usarlo sólo cuando cambio los datos en lote, como:

 public void addAll(SortedList<Entity> items) { movieList.beginBatchedUpdates(); for (int i = 0; i < items.size(); i++) { movieList.add(items.get(i)); } movieList.endBatchedUpdates(); notifyDataSetChanged(); } 

Debe utilizar en su cuenta getitem

 public int getItemCount() { if (mList!= null) return mList.size(); else return 0; } 

También refrescando la vista del reciclador utilice por favor esto

 if (recyclerView.getAdapter() == null) { recyclerView.setHasFixedSize(true); mFileListAdapter= new FileListAdapter(this); recyclerView.setAdapter(mFileListAdapter); recyclerView.setItemAnimator(new DefaultItemAnimator()); } else { mFileListAdapter.notifyDataSetChanged(); } 

Mediante el uso de esta solución no es posible resolver el problema, simplemente utilice una condición dentro de la onBindViewHolder para resolver la java.lang.IndexOutOfBoundsException

  public void onBindViewHolder(FileHolder fileHolder, final int i) { if(i < mList.size) { String name = mList.get(i); setSelected(fileHolder.itemView, mSelectedArray.get(i)); fileHolder.mTextView.setText(name); } } 

Tuve el mismo problema cuando estaba estableciendo la nueva instancia del adaptador en base a algunos criterios de selección.

He fijado mi problema usando RecyclerView.swapAdapter(adapter, true) Cuando fijamos el nuevo adaptador.

Me imagino que, para mí esta excepción viene cuando dos cosas suceden al mismo tiempo, es decir

1) Desplazamiento de recyclerview

2) conjunto de datos que se cambia

Por lo tanto, he resuelto este problema deshabilitando el desplazamiento hasta que se llama a la notificación de cambios.

 leaderAdapter.notifyDataSetChanged(); pDialog.hide(); 

Para desactivar el desplazamiento, he utilizado un diálogo de progreso, cuyo setCancelable es falso.

 pDialog = new ProgressDialog(getActivity()); pDialog.setMessage("Please wait..."); pDialog.setCancelable(false); 

El truco aquí es habilitar el desplazamiento sólo cuando el conjunto de datos ha sido actualizado.

He replicado este problema. Esto sucedió cuando eliminamos los elementos del hilo de fondo de mList pero no llamamos a notifyDataSetChanged (). Ahora, si nos desplazamos, esta excepción viene.

Java.lang.IndexOutOfBoundsException: Inconsistencia detectada. Posición 86 del elemento no válido (desplazamiento: 86) .state: 100

Inicialmente tenía 100 elementos y eliminado algunos elementos de hilo de fondo.

Parece que Recyclerview llama a getItemCount () para validar el estado.

Evite notifyDatasetHasChanged () y haga lo siguiente:

 public void setItems(ArrayList<Article> newArticles) { //get the current items int currentSize = articles.size(); //remove the current items articles.clear(); //add all the new items articles.addAll(newArticles); //tell the recycler view that all the old items are gone notifyItemRangeRemoved(0, currentSize); //tell the recycler view how many new items we added notifyItemRangeInserted(0, articles.size()); } 

No veo nada mal con el código que publicaste. Lo único extraño para mí es esta línea

 setSelected(fileHolder.itemView, mSelectedArray.get(i)); 

En su método onBindViewHolder en el adaptador .. ¿está actualizando esta matriz también cuando cambia el tamaño de su lista de elementos en la matriz?

Tuve el problema similar mientras que intento agregar el primer artículo en recyclerView con el método notifyItemInserted, así que modifiqué la función del addItem en mi adaptador como abajo y resolvió.

Extraño problema, espero que se arreglen pronto thoug!

 public void addItem(int position, TableItem item) { boolean firstEntry = false; if (items.size() == 0) { firstEntry = true; } items.add(position, item); if (firstEntry) { notifyDataSetChanged(); } else { notifyItemInserted(position); } } 

Hay una oración en el código de sonido:
/ ** * Se utiliza cuando LayoutState se construye en un estado de desplazamiento. Debe * fijar la cantidad de desplazamiento que podemos hacer sin crear una nueva * vista. Esta configuración es necesaria para un reciclaje eficiente de la vista. * / Int mScrollingOffset;

En mi caso se resolvió cambiando mRecyclerView.smoothScrollToPosition(0) a

 mRecyclerView.scrollToPosition(0) 

También tuve el mismo problema y lo he arreglado con no usar el método notifyItemRangeChanged (). Está bien explicado en

https://code.google.com/p/android/issues/detail?id=77846#c10

Tengo el mismo problema con este problema, estoy muy cansado de buscar y resolverlo. Pero he encontrado respuesta a la resolución y las excepciones no han sido rechazadas de nuevo.

 public class MyLinearLayoutManager extends LinearLayoutManager { public MyLinearLayoutManager(Context context) { super(context); } public MyLinearLayoutManager(Context context, int orientation, boolean reverseLayout) { super(context, orientation, reverseLayout); } public MyLinearLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } @Override public boolean supportsPredictiveItemAnimations() { return false; } @Override public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { //override this method and implement code as below try { super.onLayoutChildren(recycler, state); } catch (Exception e) { e.printStackTrace(); } } } 

Espero que esta respuesta sea resolver su problema.

Cree CustomLinearLayoutManager:

 public class CustomLinearLayoutManager extends LinearLayoutManager { public CustomLinearLayoutManager(Context context) { super(context); } public CustomLinearLayoutManager(Context context, int orientation, boolean reverseLayout) { super(context, orientation, reverseLayout); } public CustomLinearLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } @Override public boolean supportsPredictiveItemAnimations() { return false; } public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { try { super.onLayoutChildren(recycler, state); } catch (IndexOutOfBoundsException e) { e.printStackTrace(); } } @Override public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) { try { return super.scrollVerticallyBy(dy, recycler, state); } catch (Exception e) { e.printStackTrace(); } return 0; } } 

Intente usar una bandera booleana, inicializarla como falsa y dentro del método OnRefresh, haga que sea verdadera, borre su lista de datos si la bandera es verdadera justo antes de agregar los nuevos datos a ella y después que la hacen falsa.

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

En mi caso el problema era yo.

Mi configuración es un mecanismo Recyclerview, Adapter & Cursor / Loader.

En un momento de mi aplicación, el cargador es destruido.

supportLoaderManager.destroyLoader(LOADER_ID_EVENTS)

Esperaba que el Recyclerview mostraría una lista vacía desde que apenas suprimí su datasource. Lo que hace que el error se encuentre más complicado fue que la lista era visible y la Excepción bien conocida sólo se producía en un fling / scroll / animation.

Eso me costó unas cuantas horas. 🙂

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

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

Espero eso ayude

  • RecyclerView GridLayoutManager: cómo detectar el recuento de span?
  • Seleccione elementos en RecyclerView
  • Control de la velocidad de lanzamiento para la vista del reciclador
  • Desplácese hacia arriba en RecyclerView con LinearLayoutManager
  • ¿Cómo se relaciona la posición de un adaptador de RecyclerView con el índice de su conjunto de datos?
  • Deslizar para descartar para RecyclerView
  • Recyclerview notifyDataSetChanged (); no funciona
  • RecylcerView se desplaza a la parte superior del elemento eliminado
  • Distancia entre RecyclerView demasiado lejos
  • ¿Cómo realizar un seguimiento de los elementos seleccionados en un RecyclerView en Android?
  • Coloque el elemento de la lista del titular como facebook para cargar contenido
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.