android - recyclerview - oncreateviewholder que es
Inconsistencia detectada en RecyclerView, Cómo cambiar el contenido de RecyclerView mientras se desplaza (25)
Estoy usando
RecyclerView
para mostrar el nombre de los elementos.
Mi fila contiene
TextView
único.
Los nombres de los elementos se almacenan en
List<String> mItemList
.
Para cambiar el contenido de
RecyclerView
, reemplazo las cadenas en
mItemList
y llamo
notifyDataSetChanged
() en
RecyclerViewAdapter
.
Pero si trato de cambiar el contenido de
mItemList
mientras RecyclerView se desplaza, 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 forma correcta de cambiar el contenido de
RecyclerView
?
¿Es esto un error en
RecyclerView
?
Aquí hay un seguimiento completo 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)
Adaptador Ver código:
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;
}
}
Aunque en la respuesta aceptada se proporcionan un par de hipervínculos útiles sobre este problema,
no es cierto que este comportamiento de
RecyclerView
durante el desplazamiento sea un error
.
Si ve esta excepción, lo más probable es que se olvide de notificar al adaptador después de "cambiar" el contenido de
RecyclerView
.
La gente llama a
notifyDataSetChanged()
solo después de agregar un elemento al conjunto de datos.
Sin embargo, la inconsistencia ocurre no solo después de volver a llenar el adaptador, sino también cuando elimina un elemento o borra el conjunto de datos, debe actualizar la vista notificando al adaptador sobre 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 rellenarlo en
onStart()
.
Olvidé llamar a
notifyDataSetChanged()
después de limpiar el adaptador usando
clear()
.
Luego, cada vez que cambiaba el estado de
onStop()
a
onStart()
y desplazaba rápidamente el
RecyclerView
mientras se
onStart()
el conjunto de datos, veía esta excepción.
Si esperaba el final de la recarga sin desplazarme, no habría excepción, ya que esta vez el adaptador se puede restablecer sin problemas.
En resumen,
RecyclerView
no es coherente 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.
Debe usar en su recuento de getitem
public int getItemCount() {
if (mList!= null)
return mList.size();
else
return 0;
}
También actualizando la vista del reciclador, utilice esto
if (recyclerView.getAdapter() == null) {
recyclerView.setHasFixedSize(true);
mFileListAdapter= new FileListAdapter(this);
recyclerView.setAdapter(mFileListAdapter);
recyclerView.setItemAnimator(new DefaultItemAnimator());
} else {
mFileListAdapter.notifyDataSetChanged();
}
Al usar esta solución no puede resolver el problema, simplemente usa una condición dentro de onBindViewHolder para resolver la excepción 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);
}
}
El problema definitivamente no se debe al desplazamiento de la vista de reciclaje, sino que está relacionado con notifyDataSetChanged () . Tenía una vista de reciclador en la que cambiaba constantemente los datos, es decir, agregar y eliminar datos. Estaba llamando a notifyDataSetChanged () cada vez que agregaba elementos a mi lista, pero no actualizaba el adaptador cada vez que se eliminaba el elemento o se borraba la lista.
Entonces para arreglar el:
java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid item position 2(offset:2).state:12 at android.support.v7.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline(RecyclerView.java:5456)
Llamé a adapter.notifyDataSetChanged () después de list.clear () , donde fue necesario.
if (!myList.isEmpty()) {
myList.clear();
myListAdapter.notifyDataSetChanged();
}
Desde entonces, nunca encontré la excepción. Espero que funcione igual para otros también. :)
En mi caso el problema era yo.
Mi configuración es un mecanismo de Recyclerview, Adapter & Cursor / Loader.
En un momento de mi aplicación, el cargador se destruye.
supportLoaderManager.destroyLoader(LOADER_ID_EVENTS)
Esperaba que Recyclerview mostrara una lista vacía ya que acabo de eliminar su fuente de datos. Lo que hace que la búsqueda de errores sea más complicada es que la lista era visible y que la conocida Excepción se produjo solo en un lanzamiento / desplazamiento / animación.
Eso me costó unas horas. :)
En mi caso, se resolvió cambiando
mRecyclerView.smoothScrollToPosition(0)
a
mRecyclerView.scrollToPosition(0)
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.
Este problema está llegando a la vista de reciclaje si usa
adapter.setHasStableIds(true);
si lo configura, elimine esto y actualice su conjunto de datos dentro del adaptador;
si aún enfrenta el problema, invalide todas las vistas una vez que obtenga nuevos datos y luego actualice su conjunto de datos.
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
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());
}
He replicado este problema. Esto sucedió cuando eliminamos elementos en el hilo de fondo de mList pero no llama a notifyDataSetChanged (). Ahora si nos desplazamos Esta excepción está llegando.
java.lang.IndexOutOfBoundsException: Inconsistencia detectada. Posición de elemento no válida 86 (desplazamiento: 86). Estado: 100
Inicialmente tenía 100 elementos y eliminé algunos elementos del hilo de fondo.
Parece que Recyclerview llama a getItemCount () para validar el estado.
Me doy cuenta de que, para mí, esta excepción se produce cuando ocurren dos cosas al mismo tiempo, es decir
1) Desplazamiento de la vista de reciclaje
2) cambio de conjunto de datos
Entonces, resolví este problema deshabilitando el desplazamiento hasta que se llame a notifydatasetchanged.
leaderAdapter.notifyDataSetChanged();
pDialog.hide();
Para deshabilitar el desplazamiento, he utilizado un cuadro de 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 solo cuando el conjunto de datos se haya actualizado.
No es problema para mí. Use 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();
}
}
No veo nada incorrecto 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?
Obtuve esto para trabajar usando la sugerencia de Cocorico en una respuesta anterior (
https://.com/a/26927186/3660638
) pero hay un problema: ya que estoy usando una
SortedList
, usando notifyDataSetChanged () cada vez que hay un cambio en los datos (agregar, eliminar, etc.) te hace perder las animaciones de elementos que obtienes con
notifyItemXXXXX(position)
, así que lo que terminé haciendo fue usarlo
solo
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();
}
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;
}
}
}
);
También tuve el mismo problema y lo solucioné sin 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 buscarlo y resolverlo. Pero he encontrado una respuesta para resolver y las excepciones no se han descartado nuevamente.
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 resuelva tu problema.
Tuve el mismo problema cuando configuré una nueva instancia de adaptador según algunos criterios de selección.
He solucionado mi problema utilizando
RecyclerView.swapAdapter(adapter, true)
cuando configuramos un nuevo adaptador.
Tuve un problema similar al intentar agregar el primer elemento en recyclerView con el método notifyItemInserted, así que modifiqué la función addItem en mi adaptador como se muestra a continuación y se resolvió.
Problema extraño, ¡espero que se solucione pronto!
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);
}
}
Tuve un problema similar pero con la eliminación de los contenidos. También quería mantener las animaciones también. Terminé usando el método NoticeRemove, luego pasé 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 ...
crear 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;
}
}
haga esto cuando desee agregar una vista (como notifyData o addView o algo así)
if(isAdded()){
//
// add view like this.
//
// celebrityActionAdapter.notifyItemRangeInserted(pageSize, 10);
//
//
}
hay una oración en el código de sonido:
/ ** * Se usa cuando LayoutState se construye en un estado de desplazamiento.
Debe * establecerse la cantidad de desplazamiento que podemos hacer sin crear una nueva * vista.
Configuración necesaria para el reciclaje eficiente de la vista.
* / int mScrollingOffset;
intente usar una bandera booleana, inicialícela como falsa y dentro del método OnRefresh hágalo verdadera, borre su lista de datos si la bandera es verdadera justo antes de agregarle los nuevos datos y luego hágalo falsa.
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() ;
}
}
Editar: el error se ha solucionado ahora, si aún obtiene la misma excepción, asegúrese de actualizar su fuente de datos del adaptador solo desde el hilo principal y de llamar al método de notificación del adaptador adecuado después.
Respuesta anterior:
Parece ser un error en
RecyclerView
, se informa
here
y
here
.
Esperemos que se solucione en la próxima versión.