Android 当“废弃或附加的视图可能无法回收”时,RecyclerView 崩溃

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/26477660/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 11:34:21  来源:igfitidea点击:

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

androidandroid-recyclerview

提问by StackOverflowMaster

I'm using a simple implementation of RecyclerViewtaken from the Android website using a StaggeredGridLayoutManagerand I keep getting this error that crashes my app:

我正在使用RecyclerView从 Android 网站获取的简单实现,StaggeredGridLayoutManager我不断收到导致我的应用程序崩溃的错误:

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 ImageViewand a couple of TextViews, so I won't bother reposting my code.

简单来说,我的字面意思是从他们网站上的这个页面获取相同的实现,唯一的区别是我的网格项目的布局是一个ImageView和几个TextViews,所以我不会费心重新发布我的代码。

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

还有其他人收到此错误并知道如何处理吗?

回答by StackOverflowMaster

This error is caused if in your XML you have android:animateLayoutChangesset to true and you call notifyDataSetChanged()on the RecyclerView's adapter in the Java code.

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

So, just avoid using android:animateLayoutChangeswith RecyclerViews.

因此,请避免android:animateLayoutChanges与 RecyclerViews 一起使用。

回答by Nemanja Kovacevic

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

我也不得不处理这次崩溃,就我而言,它与android:animateLayoutChanges.

The RecyclerViewwe were building had more than one type of views in it and some were having EditTexts in them. After a while we pinned the issue to being focus related. This bug happens while recycling EditTexts and one of them is focused.

RecyclerView我们在建设有不止一种类型的意见,并进行了一些具有EditText在其中秒。过了一会儿,我们将问题归结为与焦点相关。这个错误发生在回收EditTexts 时,其中一个是重点。

Naturally we tried clearing the focus when new data is being bound to a recycled view but that didn't work until android:focusableInTouchMode="true"is set on RecycleView. Actually that is the only change that was needed in the end for this issue to go away.

自然地,当新数据绑定到回收视图时,我们尝试清除焦点,但直到android:focusableInTouchMode="true"设置为时才起作用RecycleView。实际上,这是最终消除此问题所需的唯一更改。

回答by ?zer ?zcan

I removed the android:animateLayoutChangesfrom layout property and problem has been resolved.

我删除了android:animateLayoutChangesfrom layout 属性,问题已经解决。

回答by Ram Iyer

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.

任何人都可能面临此问题的原因之一,请检查您是否已将属性android:animateLayoutChanges="true"设置为 RecyclerView。这将导致回收和重新附加 RecyclerView 的项目失败。删除它并将属性分配给 RecyclerView 的父容器,例如 LinearLayout/RelativeLayout,您应该会看到问题消失。

回答by Max

It took me two days but could not get around this, in the end, I had to disable the item prefetch.

我花了两天时间但无法解决这个问题,最后,我不得不禁用项目预取。

When setting the layout manager you can simply call

设置布局管理器时,您只需调用

mGridLayoutManager.setItemPrefetchEnabled(false);

mGridLayoutManager.setItemPrefetchEnabled(false);

It made the error go away for me. Hope it will be useful for someone.

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

回答by Anthonyeef

I met this issue this morning, but I am not facing the same reason as mentioned above.

我今天早上遇到了这个问题,但我没有遇到与上述相同的原因。

Via debug I found that the item view in my ViewHolder has mParentand 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.)

通过调试我发现我的 ViewHolder 中的项目视图有mParent并且它不是空的,在正常情况下它应该是 none(这就是日志所说的,“附加视图可能不会被回收”,我认为这意味着如果子视图已经附加到父级,它会在回收时导致失败。)

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:

但是我没有每次都手动附加子视图。我发现当我尝试在我的 ViewHolder 中膨胀子视图时它已经完成了,例如:

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

And the last parameter attachToRootshould be false.

最后一个参数attachToRoot应该是假的。

After I change it to false, I fixed my problem.

将其更改为 后false,我解决了我的问题。

By the way, I only see this crash happened when I upgrade my support library to latest version 25.0.0. Before I was using version 23.4.0 and I don't see this problem happening. I guess there should be something changed in the latest support library.

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

Hope this help.

希望这有帮助。

回答by Jaspinder Kaur

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

在使用 slimfit 粘性标题时,我遇到了这个错误。这是由于设置错误的第一个位置引起的。我在这里得到了答案

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

}

just make sure you are passing correct value for mSectionFirstPosition

只要确保您为 mSectionFirstPosition 传递正确的值

回答by user8796389

I also encountered the same errror when scrolling on the RecyclerView: then I removed animateLayoutChanges="true"in layout file for RecyclerViewthen everything worked.

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

回答by Tunji_D

In my case it happened because I had a Transitionrunning when trying to resize the RecyclerView because the software keyboard was about to show.

在我的情况下,这是因为我Transition在尝试调整 RecyclerView 的大小时正在运行,因为软件键盘即将显示。

I fixed it my excluding the RecyclerView from the Transitionby using Transition.excludeTarget(R.id.recyclerview, true);

我修复了它,我Transition通过使用 排除了 RecyclerViewTransition.excludeTarget(R.id.recyclerview, true);

回答by Archie G. Qui?ones

There are a number of reason why this exception is called. In my case it was due to animations running thats why the views are still attached and could not be removed to the view. Only when the animation is finished thus the view could be removed and recycled.

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

There are two types of animation that could affect the recyclerview recycling.

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

1) Is the RecyclerView.ItemAnimator- this should not be the problem. This should be pretty much safe to use as it check for attach and scrapped views and handle recycling properly.

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

2) android:animateLayoutChanges="true"or TransitionManager.beginDelayedTransition()or TransitionManager.go(), etc. - These animations runs on its own and take hold of the items to animate. This result to the views being force to be attached until the animation is finished. The recyclerview do not have any knowledge of these animations since it is outside its scope. Therefore the recyclerviewmight try to recycle an item thinking it could be recycled properly but the problem is that these APIs are still holding on to the views until there animation is finished.

2)android:animateLayoutChanges="true"TransitionManager.beginDelayedTransition()或 TransitionManager.go() 等 - 这些动画自行运行并控制要动画的项目。这导致视图被强制附加,直到动画完成。recyclerview 对这些动画一无所知,因为它超出了它的范围。因此,recyclerview可能会尝试回收一个项目,认为它可以正确回收,但问题是这些 API 仍然保留视图,直到动画完成。

If you are using android:animateLayoutChanges="true"or TransitionManager.beginDelayedTransition()or TransitionManager.go(), etc. simply remove the RecyclerViewand its children from the animation.

如果您正在使用android:animateLayoutChanges="true"TransitionManager.beginDelayedTransition()或 TransitionManager.go() 等,只需RecyclerView从动画中删除及其子项。

You can simply do this by taking hold of the Transitionand calling

你可以简单地通过抓住Transition和 调用来做到这一点

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

Note:

笔记:

Take note that it is important to use Transition.excludeChildren()to exclude all of Recyclerviewchildren from the animation and not just the Recyclerviewitself.

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