RecyclerView se bloquea cuando “las vistas desechadas o adjuntas no se pueden reciclar”

Estoy usando una implementación simple de RecyclerView tomada del sitio web de Android usando un StaggeredGridLayoutManager y sigo recibiendo este error que bloquea mi aplicación:

 java.lang.IllegalArgumentException: Scrapped or attached views may not be recycled. isScrap:false isAttached:true at android.support.v7.widget.RecyclerView$Recycler.recycleViewHolderInternal(RecyclerView.java:3501) at android.support.v7.widget.RecyclerView$LayoutManager.scrapOrRecycleView(RecyclerView.java:5355) at android.support.v7.widget.RecyclerView$LayoutManager.detachAndScrapAttachedViews(RecyclerView.java:5340) at android.support.v7.widget.StaggeredGridLayoutManager.onLayoutChildren(StaggeredGridLayoutManager.java:572) at android.support.v7.widget.RecyclerView.dispatchLayout(RecyclerView.java:1918) at android.support.v7.widget.RecyclerView.onLayout(RecyclerView.java:2155) at android.view.View.layout(View.java:14008) at android.view.ViewGroup.layout(ViewGroup.java:4373) at android.widget.RelativeLayout.onLayout(RelativeLayout.java:1021) at android.view.View.layout(View.java:14008) at android.view.ViewGroup.layout(ViewGroup.java:4373) at android.widget.FrameLayout.onLayout(FrameLayout.java:448) at android.view.View.layout(View.java:14008) at android.view.ViewGroup.layout(ViewGroup.java:4373) at android.widget.FrameLayout.onLayout(FrameLayout.java:448) at android.view.View.layout(View.java:14008) at android.view.ViewGroup.layout(ViewGroup.java:4373) at android.support.v7.internal.widget.ActionBarOverlayLayout.onLayout(ActionBarOverlayLayout.java:502) at android.view.View.layout(View.java:14008) at android.view.ViewGroup.layout(ViewGroup.java:4373) at android.widget.FrameLayout.onLayout(FrameLayout.java:448) at android.view.View.layout(View.java:14008) at android.view.ViewGroup.layout(ViewGroup.java:4373) at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1663) at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1521) at android.widget.LinearLayout.onLayout(LinearLayout.java:1434) at android.view.View.layout(View.java:14008) at android.view.ViewGroup.layout(ViewGroup.java:4373) at android.widget.FrameLayout.onLayout(FrameLayout.java:448) at android.view.View.layout(View.java:14008) at android.view.ViewGroup.layout(ViewGroup.java:4373) at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:1892) at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1711) at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:989) at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:4351) at android.view.Choreographer$CallbackRecord.run(Choreographer.java:749) at android.view.Choreographer.doCallbacks(Choreographer.java:562) at android.view.Choreographer.doFrame(Choreographer.java:532) at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:735) at android.os.Handler.handleCallback(Handler.java:725) at android.os.Handler.dispatchMessage(Handler.java:92) at android.os.Looper.loop(Looper.java:137) at android.app.ActivityThread.main(ActivityThread.java:5041) 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:793) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560) at dalvik.system.NativeStart.main(Native Method) 

Por simple, literalmente quiero decir que es la misma implementación tomada desde esta página en su sitio web , la única diferencia es que el diseño de mi elemento de cuadrícula es un ImageView y un par de TextView , por lo que no me molestaré en TextView mi código.

¿Alguien más está recibiendo este error y sabe cómo manejarlo?

Este error se produce si en su XML tiene android:animateLayoutChanges establecido en true y llama a notifyDataSetChanged() en el adaptador de RecyclerView en el código de Java.

Por lo tanto, solo evite usar android:animateLayoutChanges con RecyclerViews.

Tuve que lidiar con este locking también y en mi caso no tuvo nada que ver con android:animateLayoutChanges .

El RecyclerView que estábamos construyendo tenía más de un tipo de vistas y algunos tenían EditText s en ellos. Después de un tiempo, definimos el problema como relacionado con el enfoque. Este error ocurre al reciclar EditText y uno de ellos está enfocado.

Naturalmente, tratamos de aclarar el foco cuando se vinculan datos nuevos a una vista reciclada, pero eso no funcionó hasta que android:focusableInTouchMode="true" se configuró en RecycleView . De hecho, ese es el único cambio que se necesitó al final para que este problema desaparezca.

android:animateLayoutChanges el android:animateLayoutChanges de la propiedad de diseño y el problema se ha resuelto.

Entre las razones por las que cualquiera puede enfrentar este problema, verifique si ha configurado el atributo android:animateLayoutChanges="true" en RecyclerView. Esto hará que el reciclado y la reconexión de los elementos de RecyclerView fallen. Quítelo y asigne el atributo al contenedor padre de RecyclerView, como LinearLayout / RelativeLayout, y debería ver que el problema desaparezca.

Durante el uso de los cabezales adhesivos slimfit encontré este error. Fue causado debido a la configuración de la primera posición incorrecta. Tengo la respuesta aquí

 public void onBindViewHolder(MainViewHolder holder, int position) { final View itemView = holder.itemView; final LayoutManager.LayoutParams params = LayoutManager.LayoutParams.from(itemView.getLayoutParams()); params.setSlm(LinearSLM.ID); params.width = ViewGroup.LayoutParams.MATCH_PARENT; params.setFirstPosition(item.mSectionFirstPosition); itemView.setLayoutParams(params); } 

solo asegúrate de estar pasando el valor correcto para mSectionFirstPosition

Me tomó dos días pero no pude evitar esto, al final, tuve que deshabilitar la captación previa de elementos.

Al configurar el administrador de diseño, simplemente puede llamar

mGridLayoutManager.setItemPrefetchEnabled(false);

Hizo desaparecer el error para mí. Espero que sea útil para alguien.

Me encontré con este problema esta mañana, pero no estoy enfrentando la misma razón que mencioné anteriormente.

A través de la depuración encontré que la vista de elementos en mi ViewHolder tiene mParent y no es nula, lo que en el caso normal debería ser ninguno (eso es lo que dice el registro, “la vista adjunta no puede ser reciclada”, creo que significa que si el niño view ya está adjuntado a un padre, de alguna manera causaría un error al reciclar).

Pero no adjunté la vista hija cada vez manualmente. Y descubrí que está hecho cuando bash inflar la vista secundaria en mi ViewHolder, algo así como:

 layoutInflater.inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) 

Y el último parámetro attachToRoot debería ser falso.

Después de cambiarlo a false , solucioné mi problema.

Por cierto, solo veo que este locking ocurre cuando actualizo mi biblioteca de soporte a la última versión 25.0.0. Antes de usar la versión 23.4.0 y no veo que este problema ocurra. Supongo que debería haber algo cambiado en la última biblioteca de soporte.

Espero que esto ayude.

parent.addView() este problema eliminando parent.addView() en onCreateViewHolder

Este es mi código

 public MyViewHolder onCreateViewwHolder(ViewGroup parent, int viewType) { Button addButton = new Button(context); //parent.addView(addButton); return new MyViewHolder(addButton); } 

La función en android.support.v7.widget.RecyclerViewRecycler.recyclerViewHolderinternal() comprueba si mi botón ya tiene un padre o no. Lo cual si agregamos el botón al padre, también se asignará RecyclerView a su variable mParent .

En mi caso sucedió porque tenía una Transition ejecutándose cuando intentaba cambiar el tamaño de RecyclerView porque el teclado del software estaba a punto de mostrarse.

Lo arreglé y Transition.excludeTarget(R.id.recyclerview, true); el RecyclerView de la Transition usando Transition.excludeTarget(R.id.recyclerview, true);

Resolví este problema llamando

 setHasStableIds(true); 

en el constructor del adaptador y sobreescribiendo getItemId en el adaptador:

 @Override public long getItemId(int position) { return position; } 

Vi esto suceder cuando usé un objeto personalizado en el ViewHolder para el adaptador RecyclerView .

Para solucionar el problema, borre el objeto personalizado, que en mi caso era un temporizador en onViewRecycled(ViewHolder holder) para el adaptador, como se muestra a continuación:

  public void onViewRecycled(ViewHolder holder) { if(holder instanceof EntityViewHolder) { if(((EntityViewHolder)holder).timer != null) { ((EntityViewHolder) holder).timer.cancel(); } } super.onViewRecycled(holder); } 

Esto corrigió el error.

  /** * Informs the recycler whether this item can be recycled. Views which are not * recyclable will not be reused for other items until setIsRecyclable() is * later set to true. Calls to setIsRecyclable() should always be paird (one * call to setIsRecyclabe(false) should always be matched with a later call to * setIsRecyclable(true)). Pairs of calls may be nested, as the state is internally * reference-counted. * * @param recyclable Whether this item is available to be recycled. Default value * is true. * * @see #isRecyclable() */ public final void setIsRecyclable(boolean recyclable) { mIsRecyclableCount = recyclable ? mIsRecyclableCount - 1 : mIsRecyclableCount + 1; if (mIsRecyclableCount < 0) { mIsRecyclableCount = 0; if (DEBUG) { throw new RuntimeException("isRecyclable decremented below 0: " + "unmatched pair of setIsRecyable() calls for " + this); } Log.e(VIEW_LOG_TAG, "isRecyclable decremented below 0: " + "unmatched pair of setIsRecyable() calls for " + this); } else if (!recyclable && mIsRecyclableCount == 1) { mFlags |= FLAG_NOT_RECYCLABLE; } else if (recyclable && mIsRecyclableCount == 0) { mFl`enter code here`ags &= ~FLAG_NOT_RECYCLABLE; } if (DEBUG) { Log.d(TAG, "setIsRecyclable val:" + recyclable + ":" + this); } } 

1, remove : eliminar datos de la lista.

2, notifyDataSetChanged : notifyDataSetChanged ();

3, notifyItemRemoved : mostrar animación.

4, notifyItemRangeChanged : tamaño de vista de rango y volver a dibujar los viewHolders(onBindViewHolder methods)

esta excepción no es causa de

android: animateLayoutChanges

o

android: focusableInTouchMode

esta respuesta correcta final es solo porque ha configurado WRONG LayoutParams .

  nameLP = new LinearLayout.LayoutParams(context.getResources().getDisplayMetrics().widthPixels, LinearLayout.LayoutParams.WRAP_CONTENT); nameLP2 = new RecyclerView.LayoutParams(RecyclerView.LayoutParams.MATCH_PARENT, RecyclerView.LayoutParams.WRAP_CONTENT); 

el nombreLP está bien. el nombreLP2 ocurre el locking .bug está aquí.

Intento todas las respuestas de esta página. Créeme.

Yo uso com.squareup.picasso.RequestCreator

 public void into(android.widget.ImageView target, Callback callback) 

para redimensionar dinámicamente el tamaño de ImageView después de descargar la imagen desde Internet, y guarda el ancho y alto redimensionados para preservar el tamaño de la vista. LayoutParams esta excepción porque LayoutParams en un Map , y en mi onBindViewHolder, lo recuperé y lo configuré directamente en mi ImageView . Arreglo esto usando ImmutablePair para almacenar solo el tamaño de ImageView en lugar de muchos otros estados, y uso el siguiente código para restaurarlo.

 ViewGroup.LayoutParams params = image.getLayoutParams(); params.width = widthAndHeight.getLeft(); params.height = widthAndHeight.getRight(); image.setLayoutParams(params); 

Para mí, el mismo error causado por un LayoutTransition en un nivel superior de ViewGroup.

Permítanme agregar otra solución posible para este tipo de problema, por favor. Tuve el mismo problema con la biblioteca superSlim para los títulos adhesivos en RecyclerView . Utilicé MatrixCursor para establecer datos en RecyclerViewCursorAdapter . El motivo de este problema fue que las columnas de ID son iguales a 0 para todos los encabezados. Espero que eso ayude a alguien a ahorrar un par de días de depuración.

En mi caso, el problema se debió a la implementación incorrecta de este método public long getItemId(int position) (anulado desde el método RecyclerView.Adapter ).

El código anterior obtendrá dos identificadores diferentes para el mismo artículo (en mi caso, es el elemento del pie de página), después de solucionar la implementación, el problema desapareció.

En mi caso, utilicé TransitionManager.beginDelayedTransition() antes de agregar una vista en la parte superior de recyclerView. Eliminé TransitionManager.beginDelayedTransition() y no se bloqueó.

Solución alternativa si el motivo de la excepción es qué itemView tiene principal. En el código, donde has notifyItemRemoved (posición), elimina itemView de RecyclerView:

 View itemView = mRecyclerView.getLayoutManager().findViewByPosition(position); if (itemView != null && itemView.getParent() != null) { ((ViewGroup) itemView.getParent()).removeView(itemView); } notifyItemRemoved(position); 

Eliminar android:animateLayoutChanges="true" de recycleview o establecer android:animateLayoutChanges="false"

Un caso peculiar que se produjo para mí fue que tenía un miembro de vista en el adaptador y me quedé perezosa al crear una vista que no hay necesidad de hacer con la vista de reciclaje.

También va en contra de los principios de reciclaje de vistas, que al almacenar una referencia a la vista en este caso. Doy un ejemplo rápido a continuación:

 // typically we would do this in a grid view adapter: View v; // ... if(v = null){ v = LayoutInflater.inflate ...; } // Now with recycle view there is NO need to store a reference to View // and lazy instantiate. So get rid of your View v member 

Mientras que en mi caso estaba eliminando animateOnLayoutChange del recyclerView que solucionó el locking, aún necesitaba la capacidad de animar los cambios de diseño dentro del viewHolder. Para que esto funcione, LinearLayout' in the view holder needs the animateOnLayoutChange’ en verdadero, pero necesitaba notifyItemChanged a notifyItemChanged al adaptador. Esto permitió entonces que comenzaran las animaciones de transición de disposición (para expandir y contraer el viewHolder), y también evita la excepción descartada. Así que sí, evite poner animateOnLayoutChange en el recylcerView y use los diversos métodos de notificación para habilitar las animaciones predeterminadas en los cambios de tamaño de vista.