119

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?

1

28 Answers 28

192

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.

So, just avoid using android:animateLayoutChanges with RecyclerViews.

8
  • 22
    then how one can use animateLayoutChanges feature in recyclerview? Dec 24, 2014 at 11:28
  • What are you trying to achieve? Item animation? If so, the RecyclerView API supports this - take a look at the documentation: developer.android.com/reference/android/support/v7/widget/…
    – Kenneth
    Feb 9, 2015 at 8:45
  • 4
    @dhuma1981 if the item animator is set via mRecyclerView.setItemAnimator(new DefaultItemAnimator()); then animateLayoutChanges doesn't need to be true
    – Rich Ehmer
    Jun 1, 2015 at 16:39
  • RecyclerView uses DefaultItemAnimator by default.
    – Jade
    Apr 26, 2016 at 20:23
  • -,- i have this problem exactly how u described it Aug 17, 2016 at 16:23
54

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

The RecyclerView we 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.

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.

4
  • 2
    Fantastic, solved multiple focus related issues I had when using EditTexts in a RecyclerView. Thanks! Aug 25, 2015 at 13:06
  • 1
    I had ACET in recyclerview, and it crushes. This post saved me.
    – Kai Wang
    Mar 1, 2016 at 21:15
  • 1
    And i don't have edittexts in items, i do have checkboxes though. should i try android:focusableInTouchMode="true" because it happens only sometimes in some devices (rarely) and i am guessing it doesn't relate to my problem but stack-trace for crash is almost same.
    – Shivansh
    Oct 16, 2016 at 7:43
  • This was my case, but setting android:focusableInTouchMode="true" didnt help me at all. So I cleared focus in onViewDetachedFromWindow callback. public void onViewDetachedFromWindow(@NonNull RecyclerView.ViewHolder holder) { if (holder instanceof ItemViewHolder) { ItemViewHolder item = (ItemViewHolder) holder; if (item.editText.isFocused()) item.editText.clearFocus(); } }
    – artman
    Feb 25, 2020 at 5:59
26

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

3
  • I started to get this crash when I placed the android:animateLayoutChanges on my RV as well.
    – Mauker
    Jun 10, 2017 at 4:53
  • 3
    Had this flag set on the parent container (Relative Layout). Fixed the issue.
    – 1911z
    Nov 14, 2017 at 11:46
  • @1911z are u saying you had the flag on the parent container and removing it from there fixed the issue? Jan 18, 2020 at 2:27
14

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.

2
  • 1
    I saw this crash even when I have set the attribute on the parent container of the RV. Jan 18, 2020 at 2:28
  • @RamPrasadBismil Please post your code and maybe we can look at it?
    – Ram Iyer
    Jan 21, 2020 at 8:56
12

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

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

3
  • Worked for me. Thanks.
    – Vicky
    Feb 17, 2018 at 5:42
  • 1
    I'm really worry about people adopting this solution. You lose a lot disabling this flag and the bug is still in other place – Aug 21, 2018 at 17:39
  • 1
    tbh, it just made all my rows disappear. I must admit though, the crash disappeared as well XD
    – Antek
    Dec 1, 2020 at 11:23
9

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

1
  • But what if i want that transition?
    – div
    Aug 23, 2020 at 18:14
8

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

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

3
  • Welcome to StackOverflow. Could you please provide a full answer instead of just a link?
    – slfan
    Dec 11, 2015 at 7:46
  • what is item here? Dec 29, 2016 at 5:05
  • It is the list item which is to be shown in the recycler view. So basically I am saving the section first position against each list item. Jan 3, 2017 at 5:41
8

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

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.

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

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.

Hope this help.

0
7

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.

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.

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.

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

You can simply do this by taking hold of the Transition and calling

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.

1
  • Thanks! TransitionManager.beginDelayedTransition() was the cause of the problem in my case. You could update your code example with more details on how to use Transition.excludeChildren. You instantiate a transition object like: val transition = AutoTransition(), call excludeChildren(recyclerView, true) on this object and pass it to beginDelayedTransaction() as the second parameter. Apr 3, 2020 at 23:56
6

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.

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

5

I too was getting this error whenever i had animateLayoutChanges="true" in layout file for RecyclerView. Delete this attribute and error will disappear!

2
  • Please note that this question is from 2014 and the properties might have changed by now.
    – Korashen
    Sep 11, 2018 at 12:26
  • 2
    No its not changed yet Jan 9, 2019 at 7:12
4

While in my case it was removing animateOnLayoutChange from the recyclerView that fixed the crash, I still needed the ability to animate the layout changes within the viewHolder. In order to get this to work, the LinearLayout' in the view holder needs theanimateOnLayoutChange' to to true, but I needed to notifyItemChanged to the adapter. This then allowed both of the layoutTransition animations to kick off (for expanding and collapsing the viewHolder), and also avoids the scrapped exception. So yes, avoid putting the animateOnLayoutChange on the recylcerView and use the various notify methods to enable the default animations on view size changes.

2
  • I am trying to do the same thing to animate the expansion of the item, but calling notifyItemChanged in the adapter makes the item flash after the change (animation does work)! How do you prevent that?
    – Flyview
    Feb 2, 2019 at 19:37
  • For our use case we ended up swapping out our custom code using the animateOnLayoutChange, to use an expandable-layout. It accomplishes the same thing as we were trying to accomplish but way more flexible. github.com/chuross/expandable-layout
    – kingargyle
    Feb 4, 2019 at 15:05
3

I solve this problem by removing parent.addView() in onCreateViewHolder

This is my code

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

Function at android.support.v7.widget.RecyclerViewRecycler.recyclerViewHolderinternal() check whether my button has already a parent or not. Which if we add button to parent, it will be also assign RecyclerView to its mParent variable.

1
  • I think it is a new requirement, I did attach to parent in version 24 of the support of library, once updated to 25 I got the crash. Jun 8, 2017 at 8:20
2

beware not to denote ViewHolder as data class if you're using kotlin.

(credits to my workmate for noticing this)

1

I saw this happen for me when I used a custom object in the ViewHolder for the RecyclerView adapter.

To fix the issue I cleared the custom object which in my case was a timer in the onViewRecycled(ViewHolder holder) for the adapter as below:

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

This fixed the bug.

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:remove data from list.

2、notifyDataSetChanged:notifyDataSetChanged();

3、notifyItemRemoved:show animation.

4、notifyItemRangeChanged:range view size and redraw the viewHolders(onBindViewHolder methods)

1
  • i've done notifyItemRemoved when removing footer and app crashes, change it to notifyDataSetChanged and now it works ok. thank you
    – Siarhei
    Jun 4, 2017 at 23:49
1

In my case, I used the TransitionManager.beginDelayedTransition() before adding a view on top of the recyclerView. I removed the TransitionManager.beginDelayedTransition() and no crash.

1

Remove android:animateLayoutChanges="true" from recycleview or set android:animateLayoutChanges="false"

1

I had this issue because I overwrite equals() and hashcode() method of ViewHolder of RecyclerView.ViewHolder by calculating data equality and hashcode, then the recycle logic did not work and crashed, I just remove the overwrite and fixed.

0

I use com.squareup.picasso.RequestCreator

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

to dynamically resize ImageView's size after download picture from Internet, and saves the resized width and height to preserve the view size. I got this Exception because I saved LayoutParams in a Map, and in my onBindViewHolder, I retrieved it and directly set it to my ImageView. I fix this by use ImmutablePair<Integer, Integer> to only store ImageView's size rather than a lot of other states, and use following code to restore it.

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

For me the same bug with caused by a LayoutTransition on a higher level ViewGroup.

0

Let me add another possible fix for this kind of issue, please. I had the same problem with superSlim library for sticky headers in RecyclerView. I used MatrixCursor to set data to RecyclerViewCursorAdapter. The reason for this issue was ID columns equals to 0 for all headers. Hope that would help someone to save couple days of debugging.

0

In my case the problem was because of the incorrect implementing of this method public long getItemId(int position) (overrided from RecyclerView.Adapter method).

The old code will get two different ids for the same item (in my case it is the footer item), after fixing the implementation the issue has gone.

0

I solved this issue by calling

setHasStableIds(true);

in the adapter's constructor and overriding getItemId in the adapter:

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

Workaround solution if reason of Exception is what itemView has parent. In code, where you have notifyItemRemoved(position), remove itemView from RecyclerView:

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

A Peculiar case which occured for me was that i had a view member in the adapter and i was lazy instantiating a view which there is no need to do with the recycle view.

It also goes against the recycle views principles which as your storing a reference to the view in this case. I give a quick example below:

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

this exception is not cause of

android:animateLayoutChanges

or

android:focusableInTouchMode

this final correct answer is just because you set a 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);

the nameLP is OK. the nameLP2 occur the crash .bug is here.

I try all of answers of this page. trust me.

1
  • Hi @evin, how would be the right way to declare this nameLP2? Why is it wrong? Mar 17, 2021 at 20:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.