RecyclerView crashes when "scrapped or attached views may not be recycled"

I'm using a simple implementation of RecyclerView taken from the Android website using a StaggeredGridLayoutManager and I keep getting this error that crashes my app:

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)

By simple, I literally mean it's the same implementation taken from this page on their website, the only difference being is that my grid item's layout is an ImageView and a couple of TextViews, so I won't bother reposting my code.

Anyone else getting this error and know how to deal with it?

51245 次浏览

如果您在 XML 中将 android:animateLayoutChanges设置为 true,并在 Java 代码中对回收视图的适配器调用 notifyDataSetChanged(),则会导致此错误。

因此,只要避免使用 android:animateLayoutChanges和回收视图就可以了。

I've had to deal with this crash also and in my case it had nothing to do with android:animateLayoutChanges.

我们正在建设的 RecyclerView有不止一种类型的视图,其中一些有 EditText。过了一会儿,我们把这个问题归结为与焦点有关。这个错误发生在回收 EditText的时候,其中一个是重点。

当新数据被绑定到一个回收视图时,我们很自然地尝试清除焦点,但是直到 android:focusableInTouchMode="true"被设置在 RecycleView上时,这种方法才起作用。事实上,这是唯一的改变,需要在这个问题最终消失。

发生在我身上的一个特殊情况是,我在适配器中有一个视图成员,而且我懒得实例化一个视图,这个视图不需要使用回收视图。

它还违反了回收视图原则,在本例中,这些原则用于存储对视图的引用。下面我给出一个简单的例子:

// 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

当我在 ViewHolder中为 RecyclerView适配器使用自定义对象时,我看到了这种情况。

为了解决这个问题,我清除了自定义对象,在我的例子中,它是适配器 onViewRecycled(ViewHolder holder)中的一个计时器,如下所示:

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

这个修好了窃听器。

While using slimfit sticky headers i encountered this error. It was caused because of setting wrong first position. I got the answer 给你

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);


}

只要确保为 mSectionFirstposition 传递了正确的值即可

我从布局属性中删除了 android:animateLayoutChanges,问题得到了解决。

我用 com.squareup.picasso.RequestCreator

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

从 Internet 下载图片后动态调整 ImageView 的大小,并保存调整后的宽度和高度以保持视图大小。我得到这个异常是因为我在 Map中保存了 LayoutParams,在 onBindViewHolder 中,我检索了它并直接将它设置为 ImageView。我通过使用 ImmutablePair<Integer, Integer>只存储 ImageView 的大小而不是许多其他状态来修复这个问题,并使用以下代码来恢复它。

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

请允许我为这类问题添加另一种可能的修复方法。对于 RecyclerView中的粘贴头,我在 SuperSlim库中遇到了同样的问题。我使用 MatrixCursor将数据设置为 RecyclerViewCursorAdapter。出现这个问题的原因是 ID 列等于所有头的 0。希望这能帮助某人节省几天的调试时间。

对于我来说,同样的错误是由高层次的 ViewGroup 上的布局转换引起的。

今天早上我遇到了这个问题,但是我面对的不是上面提到的同样的原因。

Via debug I found that the item view in my ViewHolder has mParent and it's not null, which in normal case it should be none( that's what the log saying, "attached view may not be recycled", I think it means that if the child view is already attached to a parent, it would some how cause failure when recyclering.)

But I didn't attach the child view every time manually. And I found that it's done when I try to inflate the child view in my ViewHolder, something like:

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

And the last parameter attachToRoot should be false.

当我把它改成 false后,我解决了我的问题。

顺便说一下,只有在我将支持库升级到最新版本25.0.0时,才会发生这种崩溃。在我使用版本23.4.0之前,我没有看到这个问题发生。我想在最新的支持库中应该有一些改变。

Hope this help.

我通过删除 onCreateViewHolder中的 parent.addView()来解决这个问题

这是我的原则

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

函数在 android.support.v7.widget.RecyclerViewRecycler.recyclerViewHolderinternal()检查我的按钮是否已经有父级。如果我们将按钮添加到父级,它也会将 RecyclerView赋给它的 mParent变量。

    /**
* 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);
}
}

从列表中删除数据。

2、notifyDataSetChanged:notifyDataSetChanged();

3、 notifyItemRemoved: 显示动画。

4、 notifyItemRangeChanged: 范围视图大小和重画 viewHolders(onBindViewHolder methods)

在我的例子中,问题是由于这个方法 public long getItemId(int position)的不正确实现(从 RecyclerView.Adapter方法重写)。

在修复了实现之后,旧代码将为同一个项目获得两个不同的 id (在我的例子中是页脚项目)。

Among the reasons that anyone can face this issue, check if you have set the attribute android:animateLayoutChanges="true"to the RecyclerView. This will cause the recycling and reattaching the RecyclerView's items to fail. Remove it and assign the attribute to the RecyclerView's parent container, such as a LinearLayout/RelativeLayout and you should see the problem go away.

在我的例子中,我在添加视图之前使用了 TransitionManager.beginDelayedTransition()。我移除了 TransitionManager.beginDelayedTransition()没有发生撞击。

我通过打电话解决了这个问题

setHasStableIds(true);

在适配器的构造函数中并在适配器中重写 getItemId:

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

我花了两天时间,但无法绕过这一点,最后,我不得不禁用项目预取。

当设置布局管理器时,您可以简单地调用

mGridLayoutManager.setItemPrefetchEnabled(false);

它使我的错误消失了。希望对某些人有用。

解决方案,如果异常的原因是 itemView 的父。 在代码中,如果您有 notifyItemRemoved (位置) ,请将 itemView 移除回收视图:

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

从回收视图中删除 android:animateLayoutChanges="true"或设置 android:animateLayoutChanges="false"

在我的例子中发生这种情况是因为我在试图调整回收视图大小时运行了 Transition,因为软件键盘即将显示。

我修正了我的不包括从 Transition使用 Transition.excludeTarget(R.id.recyclerview, true);的回收视图

这个例外不是

Android: animateLayoutChanges

或者

可聚焦触摸模式

这个最终的正确答案只是因为你设置了一个 错误的布局参数

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

Name LP 是 OK。 名称 LP2发生崩溃。错误是在这里。

我试过这页的所有答案,相信我。

虽然在我的情况下,它是删除 animateOnLayoutChange从回收视图,修复崩溃,我仍然需要的能力,以动画的布局变化内的 viewHolder。为了让这个工作,LinearLayout' in the view holder needs theanimateOnLayoutChange’为真,但我需要 notifyItemChanged到适配器。这样就可以启动布局过渡动画(用于展开和折叠 viewHolder) ,同时也避免了废弃的异常。因此,是的,避免将 animateOnLayoutChange 放在 recylcerView 上,并使用各种通知方法在视图大小更改时启用默认动画。

每当我在回收视图的布局文件中有 animateLayoutChanges = “ true”时,我也会得到这个错误。 删除此属性,错误将消失!

我有这个问题,因为我覆盖 RecyclerViewViewHolderequals()hashcode()方法。 ViewHolder 通过计算数据等式和哈希码,然后回收逻辑不工作,崩溃,我只是删除覆盖和修复。

我也遇到了同样的错误时,在 RecyclerView上滚动: 然后我删除了布局文件中的 animateLayoutChanges="true"RecyclerView然后一切工作。

调用此异常的原因有很多。在我的情况下,这是由于动画运行,这就是为什么视图仍然附加,不能被删除到视图。只有当动画完成,因此视图可以删除和回收。

有两种类型的动画,可能会影响回收视图回收。

1)是 RecyclerView.ItemAnimator-这不应该是问题。这应该是相当安全的使用,因为它检查附加和废弃的视图,并处理回收正确。

2) android:animateLayoutChanges="true"或者 TransitionManager.beginDelayedTransition()或者 TrantionManager.go () ,等等-这些动画会自己运行并且抓住项目来制作动画。这个结果对视图被强制附加,直到动画完成。回收视图没有任何关于这些动画的知识,因为它超出了它的范围。因此,recyclerview可能会尝试回收一个项目,认为它可以被适当地回收,但问题是,这些 API 仍然持有的意见,直到有动画完成。

如果你使用的是 android:animateLayoutChanges="true"或者 TransitionManager.beginDelayedTransition()或者 TrantionManager.go ()等等,只需要从动画中删除 RecyclerView及其子元素。

您可以通过控制 Transition并调用

Transition.excludeChildren(yourRecyclerView, true)
Transition.excludeTarget(yourRecyclerView, true)

注:

请注意,重要的是使用 Transition.excludeChildren()排除所有的 Recyclerview子动画,而不仅仅是 Recyclerview本身。

如果使用 kotlin,请注意不要将 ViewHolder表示为 data class

(credits to my workmate for noticing this)