J'utilise une implémentation simple de RecyclerView
tirée du site Web Android en utilisant un StaggeredGridLayoutManager
et je continue à avoir cette erreur qui bloque mon application:
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)
Par simple, je veux dire littéralement que c'est la même implémentation tirée de cette page sur leur site web , la seule différence est que la disposition de mon élément de grille est un ImageView
et un couple de TextView
s, je ne vais donc pas m'inquiéter de republier mon code .
Quelqu'un d'autre qui a cette erreur et sait comment y faire face?
Cette erreur est due au fait que Android:animateLayoutChanges
est défini sur true dans votre code XML et que vous appelez notifyDataSetChanged()
sur l'adaptateur de RecyclerView dans le code Java.
Donc, évitez simplement d'utiliser Android:animateLayoutChanges
avec RecyclerViews.
J'ai eu à faire face à cet accident aussi et dans mon cas, cela n'avait rien à voir avec Android:animateLayoutChanges
.
La RecyclerView
que nous construisions avait plus d'un type de vues et certaines avaient EditText
s. Après un certain temps, nous avons déterminé que la question était liée à la concentration. Ce bogue survient lors du recyclage de EditText
s et l’un d’eux est ciblé.
Naturellement, nous avons essayé de supprimer le focus lorsque de nouvelles données étaient liées à une vue recyclée, mais cela ne fonctionnait pas tant que Android:focusableInTouchMode="true"
n'était pas défini sur RecycleView
. En réalité, c’est le seul changement nécessaire à la fin pour que cette question disparaisse.
J'ai supprimé la propriété Android:animateLayoutChanges
de la mise en page et le problème a été résolu.
Parmi les raisons pour lesquelles tout le monde peut être confronté à ce problème, vérifiez si vous avez défini l'attribut Android:animateLayoutChanges="true"
sur RecyclerView. Cela entraînera l'échec du recyclage et de la réinsertion des éléments de RecyclerView. Supprimez-le et attribuez l'attribut au conteneur parent de RecyclerView, tel que LinearLayout/RelativeLayout, et le problème devrait disparaître.
En utilisant les en-têtes collants slimfit, j'ai rencontré cette erreur. Cela était dû à la mauvaise position de la première position. J'ai eu la réponse ici
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);
}
assurez-vous simplement que vous transmettez la valeur correcte pour mSectionFirstPosition
J'ai rencontré ce problème ce matin, mais je ne suis pas confronté à la même raison que celle mentionnée ci-dessus.
Via debug, j’ai trouvé que la vue d’élément de mon ViewHolder avait mParent
et qu’elle n’est pas nulle, ce qui devrait normalement être nul (c’est ce que dit le journal, "la vue attachée ne peut pas être recyclée", cela signifie que si l’enfant Si la vue est déjà attachée à un parent, cela pourrait en quelque sorte causer des échecs lors du recyclage.)
Mais je n'ai pas attaché manuellement la vue enfant à chaque fois. Et j’ai trouvé que c’était fait lorsque j’essayais de gonfler la vue enfant dans mon ViewHolder, quelque chose comme:
layoutInflater.inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot)
Et le dernier paramètre attachToRoot
devrait être faux.
Après l'avoir modifié en false
, j'ai résolu mon problème.
En passant, je ne vois que cet incident s'est produit lorsque j'ai mis à niveau ma bibliothèque de support vers la dernière version 25.0.0. Avant, j'utilisais la version 23.4.0 et je ne vois pas ce problème se produire. Je suppose qu'il devrait y avoir quelque chose de changé dans la dernière bibliothèque de support.
J'espère que cette aide.
Dans mon cas, cela s’est produit parce que j’avais une Transition
exécutée lorsque je tentais de redimensionner le RecyclerView car le clavier du logiciel était sur le point de s’afficher.
J'ai corrigé mon exclusion de RecyclerView de Transition
en utilisant Transition.excludeTarget(R.id.recyclerview, true);
Moi aussi, je recevais cette erreur chaque fois que j'avais animateLayoutChanges = "true" dans le fichier de mise en page pour RecyclerView . Supprimez cet attribut et l'erreur disparaîtra!
Je résous ce problème en supprimant parent.addView()
dans onCreateViewHolder
C'est mon code
public MyViewHolder onCreateViewwHolder(ViewGroup parent, int viewType) {
Button addButton = new Button(context);
//parent.addView(addButton);
return new MyViewHolder(addButton);
}
Fonction à Android.support.v7.widget.RecyclerViewRecycler.recyclerViewHolderinternal()
vérifier si mon bouton a déjà un parent ou non. Si nous ajoutons un bouton au parent, nous assignerons également RecyclerView
à sa variable mParent
.
J'ai résolu ce problème en appelant
setHasStableIds(true);
dans le constructeur de l'adaptateur et en remplaçant getItemId
dans l'adaptateur:
@Override
public long getItemId(int position) {
return position;
}
Alors que dans mon cas, il supprimait animateOnLayoutChange
de recyclerView qui corrigeait l'incident, il me fallait tout de même pouvoir animer les modifications de présentation dans le viewHolder. Pour que cela fonctionne, la valeur LinearLayout' in the view holder needs the
animateOnLayoutChange 'est définie sur true, mais je devais ajouter notifyItemChanged
à l'adaptateur. Cela a ensuite permis aux deux animations de layoutTransition de démarrer (pour développer et réduire le viewHolder) et d'éviter également l'exception supprimée. Alors oui, évitez de placer animateOnLayoutChange sur recylcerView et utilisez les différentes méthodes de notification pour activer les animations par défaut lors des changements de taille de vue.
J'ai également rencontré le même errror lors du défilement sur la RecyclerView
: puis j'ai supprimé animateLayoutChanges="true"
dans le fichier de présentation pour RecyclerView
, puis tout a fonctionné.
Dans mon cas, j’ai utilisé la TransitionManager.beginDelayedTransition()
avant d’ajouter une vue au-dessus de recyclerView. J'ai enlevé le TransitionManager.beginDelayedTransition()
et pas de crash.
C'est ce qui m'est arrivé lorsque j'ai utilisé un objet personnalisé dans ViewHolder
pour l'adaptateur RecyclerView
.
Pour résoudre le problème, j'ai effacé l'objet personnalisé qui, dans mon cas, était une minuterie dans onViewRecycled(ViewHolder holder)
pour l'adaptateur, comme indiqué ci-dessous:
public void onViewRecycled(ViewHolder holder) {
if(holder instanceof EntityViewHolder) {
if(((EntityViewHolder)holder).timer != null) {
((EntityViewHolder) holder).timer.cancel();
}
}
super.onViewRecycled(holder);
}
Cela a corrigé le bug.
1 、 remove
: supprimer les données de la liste.
2 、 notifyDataSetChanged
: notifyDataSetChanged ();
3 、 notifyItemRemoved
: affiche l'animation.
4 、 notifyItemRangeChanged
: ajustez la taille de la vue et redessinez le viewHolders(onBindViewHolder methods)
/**
* 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 paired (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);
}
}
cette exception n'est pas cause de
Android: animateLayoutChanges
ou
Android: focusableInTouchMode
cette réponse correcte finale est juste parce que vous définissez un 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);
le nomLP est OK . le nomLP2 se produit le crash .bug est ici.
J'essaie toutes les réponses de cette page. croyez-moi.
Il existe plusieurs raisons pour lesquelles cette exception est appelée. Dans mon cas, cela était dû aux animations exécutées, c'est pourquoi les vues sont toujours attachées et ne peuvent pas être supprimées de la vue. Une fois l'animation terminée, la vue peut être supprimée et recyclée.
Il existe deux types d'animation pouvant affecter le recyclage de recyclerview.
1) Est-ce que le RecyclerView.ItemAnimator
- cela ne devrait pas être le problème. Cela devrait être assez sûr à utiliser car il vérifie les vues attachées et mises au rebut et gère correctement le recyclage.
2) Android:animateLayoutChanges="true"
ou TransitionManager.beginDelayedTransition()
ou TransitionManager.go (), etc. - Ces animations s'exécutent seules et prennent en charge les éléments à animer. Ce résultat sur les vues étant forcé d'être attaché jusqu'à la fin de l'animation. Recyclerview n'a aucune connaissance de ces animations car elles sortent de son cadre. Par conséquent, recyclerview
peut essayer de recycler un élément en pensant qu'il peut être recyclé correctement, mais le problème est que ces API conservent les vues jusqu'à la fin de l'animation.
Si vous utilisez Android:animateLayoutChanges="true"
ou TransitionManager.beginDelayedTransition()
ou TransitionManager.go (), etc., supprimez simplement la variable RecyclerView
et ses enfants de l'animation.
Vous pouvez le faire simplement en prenant la Transition
et en appelant
Transition.excludeChildren(yourRecyclerView, true)
Transition.excludeTarget(yourRecyclerView, true)
Remarque:
Notez qu'il est important d'utiliser Transition.excludeChildren()
pour exclure tous les enfants Recyclerview
de l'animation et pas seulement la Recyclerview
elle-même.
J'ai eu ce problème parce que j'écrase les méthodes equals()
et hashcode()
de ViewHolder
de RecyclerView
. ViewHolder en calculant l'égalité des données et le hashcode, la logique de recyclage ne fonctionnait pas et se bloquait, je supprime simplement l'écrasement et le corrige.
Un cas particulier qui s'est produit pour moi est que j'avais un membre de la vue dans l'adaptateur et que j'étais paresseux pour créer une vue qu'il n'est pas nécessaire de faire avec la vue de recyclage.
Cela va également à l’encontre des principes de recyclage des vues qui vous permettent de stocker une référence à la vue dans ce cas. Je donne un exemple rapide ci-dessous:
// 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
Solution de contournement si la raison de l'exception est ce que itemView a parent. Dans le code, où vous avez notifyItemRemoved (position), supprimez itemView de RecyclerView:
View itemView = mRecyclerView.getLayoutManager().findViewByPosition(position);
if (itemView != null && itemView.getParent() != null) {
((ViewGroup) itemView.getParent()).removeView(itemView);
}
notifyItemRemoved(position);
J'utilise com.squareup.picasso.RequestCreator
public void into(Android.widget.ImageView target,
Callback callback)
redimensionner dynamiquement la taille d'ImageView après le téléchargement d'une image à partir d'Internet et enregistre la largeur et la hauteur redimensionnées pour conserver la taille de la vue. J'ai eu cette exception parce que j'ai enregistré LayoutParams
dans une Map
et que dans mon onBindViewHolder, je l'ai récupérée et directement définie à ma ImageView
. Je résous ce problème en utilisant ImmutablePair<Integer, Integer>
pour ne stocker que la taille de ImageView plutôt que de nombreux autres états, et utilise le code suivant pour le restaurer.
ViewGroup.LayoutParams params = image.getLayoutParams();
params.width = widthAndHeight.getLeft();
params.height = widthAndHeight.getRight();
image.setLayoutParams(params);
Permettez-moi d'ajouter une autre solution possible à ce type de problème, s'il vous plaît. J'ai eu le même problème avec superSlim bibliothèque pour les en-têtes collants dans RecyclerView
. J'ai utilisé MatrixCursor
pour définir les données sur RecyclerViewCursorAdapter
. La raison de ce problème était que les colonnes d'ID étaient égales à 0
pour tous les en-têtes. J'espère que cela aiderait quelqu'un à économiser quelques jours de débogage.
Dans mon cas, le problème était dû à une implémentation incorrecte de cette méthode public long getItemId(int position)
(remplacée par la méthode RecyclerView.Adapter
).
L'ancien code aura deux identifiants différents pour le même élément (dans mon cas, il s'agit de l'élément de bas de page).
Supprimez Android:animateLayoutChanges="true"
de recycleview ou définissez Android:animateLayoutChanges="false"
Pour moi, le même bug causé par LayoutTransition sur un ViewGroup de niveau supérieur.