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
RecyclerView crashes when "scrapped or attached views may not be recycled"
提问by StackOverflowMaster
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:
我正在使用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 ImageView
and a couple of TextView
s, so I won't bother reposting my code.
简单来说,我的字面意思是从他们网站上的这个页面获取相同的实现,唯一的区别是我的网格项目的布局是一个ImageView
和几个TextView
s,所以我不会费心重新发布我的代码。
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:animateLayoutChanges
set 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:animateLayoutChanges
with 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 RecyclerView
we were building had more than one type of views in it and some were having EditText
s in them. After a while we pinned the issue to being focus related. This bug happens while recycling EditText
s and one of them is focused.
在RecyclerView
我们在建设有不止一种类型的意见,并进行了一些具有EditText
在其中秒。过了一会儿,我们将问题归结为与焦点相关。这个错误发生在回收EditText
s 时,其中一个是重点。
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:animateLayoutChanges
from layout property and problem has been resolved.
我删除了android:animateLayoutChanges
from 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 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.)
通过调试我发现我的 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 attachToRoot
should 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 RecyclerView
then everything worked.
在滚动RecyclerView
:时我也遇到了同样的错误,然后我animateLayoutChanges="true"
在布局文件中删除了RecyclerView
然后一切正常。
回答by Tunji_D
In my case it happened because I had a Transition
running 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 Transition
by 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 recyclerview
might 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 RecyclerView
and its children from the animation.
如果您正在使用android:animateLayoutChanges="true"
或TransitionManager.beginDelayedTransition()
或 TransitionManager.go() 等,只需RecyclerView
从动画中删除及其子项。
You can simply do this by taking hold of the Transition
and 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 Recyclerview
children from the animation and not just the Recyclerview
itself.
请注意,重要的是使用从动画中Transition.excludeChildren()
排除所有Recyclerview
子项而不仅仅是其Recyclerview
本身。