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


114

我使用的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)  

简而言之,我的意思是说这与他们网站上此页面上的实现相同,唯一的区别是我的网格项目的布局是an ImageView和几个TextViews,因此我不会费心重新发布我的代码。

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


你有解决办法吗?
Pratik Butani 2015年

Answers:


191

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

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


22
那么如何在recyclerview中使用animateLayoutChanges功能?
dhuma1981'2014/

您想达到什么目的?项目动画?如果是这样,RecyclerView API支持这一点-看看文档:developer.android.com/reference/android/support/v7/widget/...
肯尼斯

4
@ dhuma1981(如果通过mRecyclerView.setItemAnimator(new DefaultItemAnimator())设置了项目动画制作器;那么animateLayoutChanges不必为真
Rich Ehmer

RecyclerViewDefaultItemAnimator默认使用。
本杰明

-,-我完全按照您的描述来解决这个问题
Ninja Coding

52

我还不得不处理这次崩溃,就我而言,它与无关android:animateLayoutChanges

RecyclerView我们在建设有不止一种类型的意见,并进行了一些具有EditText在其中秒。一段时间后,我们将问题固定为与焦点相关。回收EditTexts 时会发生此错误,并且其中一个是关注的。

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


2
太棒了,解决了我在RecyclerView中使用EditTexts时遇到的与焦点相关的多个问题。谢谢!
拉比·杰拉迪

1
我在recyclerview中使用了ACET,但结果很糟糕。这篇文章救了我。
凯旺

而且我在项目中没有edittexts,但是我确实有复选框。我应该尝试一下,android:focusableInTouchMode="true"因为它仅在某些设备中偶尔发生(很少),而且我猜想它与我的问题无关,但是崩溃的堆栈跟踪几乎相同。
Shivansh '16

这是我的情况,但是设置android:focusableInTouchMode="true"根本没有帮助我。因此,我清除了对onViewDetachedFromWindow回调的关注。public void onViewDetachedFromWindow(@NonNull RecyclerView.ViewHolder holder) { if (holder instanceof ItemViewHolder) { ItemViewHolder item = (ItemViewHolder) holder; if (item.editText.isFocused()) item.editText.clearFocus(); } }
artman

24

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


当我将android:animateLayoutChangesRV 放在我的RV上时,也开始出现此崩溃。
Mauker

2
是否在父容器上设置了此标志(相对布局)。解决了该问题。
1911z

@ 1911z您是说您在父容器上有该标志并将其从那里删除解决了该问题吗?
RamPrasadBismil

14

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


即使在RV的父容器上设置了属性,我也看到了崩溃。
RamPrasadBismil

@RamPrasadBismil请发布您的代码,也许我们可以看一下?
Ram Iyer

12

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

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

mGridLayoutManager.setItemPrefetchEnabled(false);

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


为我工作。谢谢。
Vicky '18

1
我真的很担心人们会采用这种解决方案。您失去了大量禁用此标志的权限,并且该错误仍在其他地方–
Felipe Castilhos

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传递了正确的值


欢迎使用StackOverflow。您能否提供完整的答案,而不仅仅是链接?
slfan 2015年

这是item什么
错误发生

这是要在回收者视图中显示的列表项。因此,基本上,我将节的第一位置保存在每个列表项中。
Jaspinder Kaur's

8

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

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

但是我并不是每次都手动附加子视图。而且我发现,当我尝试在ViewHolder中给子视图充气时,它就完成了,例如:

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

最后一个参数attachToRoot应该为false。

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

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

希望能有所帮助。


8

RecyclerView:上滚动时,我也遇到了相同的错误,然后我animateLayoutChanges="true"在布局文件中删除了RecyclerView所有内容。


6

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

我修复了Transition通过使用 排除了RecyclerView的问题Transition.excludeTarget(R.id.recyclerview, true);


6

调用此异常有多种原因。在我的情况下,这是由于运行动画,这就是为什么视图仍附加且无法删除到视图的原因。仅当动画结束时,才能删除和回收视图。

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

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

2)android:animateLayoutChanges="true"TransitionManager.beginDelayedTransition()TransitionManager.go()等。-这些动画独立运行,并抓住要进行动画处理的项目。这导致强制将视图附加到动画完成之前。recyclerview不了解这些动画,因为它不在其范围内。因此,recyclerview可能会尝试回收某个项目,认为它可以正确回收,但是问题是这些API仍会保留视图,直到动画完成为止。

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

您只需按住Transition并调用即可

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

注意:

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


谢谢!就我而言,TransitionManager.beginDelayedTransition()是问题的原因。您可以使用更多详细信息更新代码示例Transition.excludeChildren。实例化一个过渡对象,例如:val transition = AutoTransition(),调用excludeChildren(recyclerView, true)该对象并将其传递给beginDelayedTransaction() as the second parameter
Danilo Prado

5

每当我在RecyclerView的布局文件中有animateLayoutChanges =“ true”时,我也都会收到此错误。删除该属性,错误将消失!


请注意,这个问题来自2014年,现在属性可能已更改。
Korashen

2
没有它没有改变
桑杰Kushwah

4

在我看来,它animateOnLayoutChange已从可修复崩溃的recyclerView中删除,但我仍然需要能够在viewHolder中对布局更改进行动画处理。为了使其正常工作,LinearLayout' in the view holder needs theanimateOnLayoutChange'设置为true,但我需要notifyItemChanged使用适配器。然后,这将允许两个layoutTransition动画开始(用于展开和折叠viewHolder),并且还避免了报废的异常。因此,是的,请避免将animateOnLayoutChange放在recylcerView上,并使用各种notify方法来启用视图大小更改时的默认动画。


我正在尝试做同样的事情以动画化该项目的扩展,但是在适配器中调用notifyItemChanged会使该项目在更改后闪烁(动画确实起作用)!您如何预防呢?
Flyview

对于我们的用例,我们最终使用animateOnLayoutChange交换了自定义代码,以使用expandable-layout。它完成了与我们尝试完成的相同的操作,但是方式更加灵活。github.com/chuross/expandable-layout
kingargyle

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


我认为这是一个新要求,我确实在库支持的版本24中将其附加到父级,一旦更新到25,我便崩溃了。
Kirill Kulakov

1

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

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

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

这修复了该错误。


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

1

1 remove,:从列表中删除数据。

2,notifyDataSetChanged:notifyDataSetChanged();

3 notifyItemRemoved,:显示动画。

4 notifyItemRangeChanged,:查看尺寸并重新绘制viewHolders(onBindViewHolder methods)


我已经完成了notifyItemRemoved删除footer和应用崩溃的操作,将其更改为notifyDataSetChanged现在可以正常使用了。谢谢
Siarhei

1

就我而言,我TransitionManager.beginDelayedTransition()在将数据添加到recyclerView顶部之前使用了。我删除了TransitionManager.beginDelayedTransition()并且没有崩溃。


1

我通过致电解决了这个问题

setHasStableIds(true);

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

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


0

我用 com.squareup.picasso.RequestCreator

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

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

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

0

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


0

请允许我为这种问题添加另一个可能的修复。对于superSlim库中的粘性标头,我遇到了同样的问题RecyclerView。我曾经MatrixCursor将数据设置为RecyclerViewCursorAdapter。此问题的原因是ID列等于0所有标头。希望这可以帮助某人节省几天的调试时间。


0

在我的情况下,问题是由于此方法的实现不正确public long getItemId(int position)(从RecyclerView.Adapter方法中重写)。

修复实现后,旧代码将为同一项目(在我的情况下为页脚项目)获得两个不同的ID。


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

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

0

此异常不是导致

android:animateLayoutChanges

要么

android:focusableInTouchMode

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

nameLP可以。nameLP2发生崩溃.bug在这里。

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


0

我有这个问题,因为我重写equals()hashcode()方法ViewHolderRecyclerView通过计算数据的平等和hashCode,.ViewHolder然后循环逻辑没有工作和崩溃,我只是删除覆盖并固定。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.