118

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

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)  

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

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

4

28 回答 28

193

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

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

于 2014-10-21T06:01:26.270 回答
53

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

我们正在构建的RecyclerView视图中包含不止一种类型的视图,其中一些视图中包含EditTexts。一段时间后,我们将问题归结为与焦点相关。这个错误发生在回收EditTexts 并且其中一个被聚焦时。

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

于 2015-01-07T21:23:32.037 回答
26

android:animateLayoutChanges从布局属性中删除了,问题已经解决。

于 2016-01-19T21:21:45.210 回答
13

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

于 2017-03-22T14:37:07.423 回答
12

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

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

mGridLayoutManager.setItemPrefetchEnabled(false);

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

于 2017-09-16T10:52:30.900 回答
9

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

于 2018-11-05T14:12:44.910 回答
8

在使用 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);

}

只需确保您为 mSectionFirstPosition 传递了正确的值

于 2015-12-11T07:24:59.750 回答
8

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

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

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

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

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

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

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

希望这有帮助。

于 2016-11-25T07:10:35.053 回答
7

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

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

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

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

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

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

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

笔记:

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

于 2019-03-15T01:42:54.783 回答
6

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

Transition我通过使用 将其排除在 RecyclerView 之外Transition.excludeTarget(R.id.recyclerview, true);

于 2017-11-29T22:39:06.410 回答
5

每当我在 RecyclerView 的布局文件中有 animateLayoutChanges="true" 时,我也会收到此错误。删除这个属性,错误就会消失!

于 2018-09-11T11:55:42.110 回答
4

虽然在我的情况下,它正在animateOnLayoutChange从修复崩溃的 recyclerView 中删除,但我仍然需要能够为 viewHolder 中的布局更改设置动画。为了让它工作,LinearLayout' in the view holder needs theanimateOnLayoutChange' 为真,但我需要notifyItemChanged适配器。然后,这允许两个 layoutTransition 动画启动(用于展开和折叠 viewHolder),并且还避免了报废异常。所以是的,避免将 animateOnLayoutChange 放在 recyclerView 上,并使用各种通知方法来启用视图大小更改的默认动画。

于 2018-05-02T13:22:12.427 回答
3

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

这是我的代码

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变量。

于 2017-01-13T19:08:57.193 回答
2

请注意不要表示您使用的是 kotlin ViewHolderdata class

(感谢我的同事注意到这一点)

于 2020-12-01T14:03:35.010 回答
1

我遇到了这个问题,因为我通过计算数据相等性和哈希码覆盖了.ViewHolderequals()hashcode()方法,然后回收逻辑不起作用并崩溃,我只是删除了覆盖并修复了。ViewHolderRecyclerView

于 2018-10-24T04:53:17.610 回答
1

1、<code>remove:从列表中删除数据。

2、<code>notifyDataSetChanged:notifyDataSetChanged();

3、<code>notifyItemRemoved:显示动画。

4、<code>notifyItemRangeChanged:范围视图大小并重绘viewHolders(onBindViewHolder methods)

于 2017-03-03T06:08:42.967 回答
1

就我而言,我使用了TransitionManager.beginDelayedTransition()在 recyclerView 之上添加视图之前的方法。我删除了TransitionManager.beginDelayedTransition(),没有崩溃。

于 2017-05-31T01:56:28.620 回答
1

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

于 2017-10-11T10:15:33.913 回答
1

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

这修复了错误。

于 2015-09-04T06:28:52.940 回答
1
    /**
     * 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);
        }
    }
于 2017-03-03T05:58:32.017 回答
0

发生在我身上的一个特殊情况是我在适配器中有一个视图成员,并且我懒惰地实例化一个不需要与回收视图有关的视图。

这也违反了回收视图原则,在这种情况下,您存储对视图的引用。我在下面给出一个简单的例子:

// 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
于 2015-02-20T15:58:46.753 回答
0

我通过调用解决了这个问题

setHasStableIds(true);

在适配器的构造函数中并getItemId在适配器中覆盖:

@Override
public long getItemId(int position) {
    return position;
}
于 2017-06-15T21:29:14.700 回答
0

在我的情况下,问题是由于此方法的错误实现public long getItemId(int position)(从方法覆盖RecyclerView.Adapter)。

旧代码将为同一个项目(在我的情况下是页脚项目)获得两个不同的 id,在修复实现后问题已经消失。

于 2017-03-09T14:16:07.187 回答
0

如果异常原因是 itemView 的父项,则解决方法解决方案。在有 notifyItemRemoved(position) 的代码中,从 RecyclerView 中删除 itemView:

View itemView = mRecyclerView.getLayoutManager().findViewByPosition(position);
if (itemView != null && itemView.getParent() != null) {
    ((ViewGroup) itemView.getParent()).removeView(itemView);
}
notifyItemRemoved(position);
于 2017-09-25T08:50:34.013 回答
0

请让我为此类问题添加另一个可能的解决方案。我对superSlim库的粘性标题有同样的问题RecyclerView。我曾经MatrixCursor将数据设置为RecyclerViewCursorAdapter. 此问题的原因是 ID 列等于0所有标题。希望这可以帮助某人节省几天的调试时间。

于 2016-03-15T02:37:01.357 回答
0

对我来说,由更高级别 ViewGroup 上的 LayoutTransition 引起的相同错误。

于 2016-08-05T12:33:20.437 回答
0

我用com.squareup.picasso.RequestCreator

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

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

ViewGroup.LayoutParams params = image.getLayoutParams();
params.width = widthAndHeight.getLeft();
params.height = widthAndHeight.getRight();
image.setLayoutParams(params);
于 2016-01-21T05:21:10.613 回答
0

此异常不是导致

android:animateLayoutChanges

或者

android:focusableInTouchMode

这个最终的正确答案只是因为您设置了错误的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);

名称LP是可以的。nameLP2 发生崩溃 .bug 在这里。

我尝试了此页面的所有答案。相信我。

于 2017-12-16T03:19:57.807 回答