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

28

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?

Atonal answered 21/10, 2014 at 1:45 Comment(1)
You got any solution?Ankylostomiasis
A
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.

Atonal answered 21/10, 2014 at 6:1 Comment(8)
then how one can use animateLayoutChanges feature in recyclerview?Unamerican
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/…Excisable
@dhuma1981 if the item animator is set via mRecyclerView.setItemAnimator(new DefaultItemAnimator()); then animateLayoutChanges doesn't need to be trueLaureenlaurel
RecyclerView uses DefaultItemAnimator by default.Donaldson
-,- i have this problem exactly how u described itUndersize
Couldn't find a way to animate column removal in RecyclerView with GridLayoutManager, thought to use animateLayoutChanges but also got this crash...Scheller
Please see my answer to set animateLayoutChanges with RecyclerViews and still avoid the crash.Aureomycin
This doesn't seem to happen anymore, when using implementation "com.android.support:recyclerview-v7:$28.0.0" and possibly implementation 'androidx.recyclerview:recyclerview-v7:1.0.0'. There are no longer crashes when using android:animateLayoutChanges in the layout.Homosexuality
D
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.

Dimmer answered 7/1, 2015 at 21:23 Comment(4)
Fantastic, solved multiple focus related issues I had when using EditTexts in a RecyclerView. Thanks!Syncopated
I had ACET in recyclerview, and it crushes. This post saved me.Hyperphagia
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.Tucci
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(); } }Dominant
T
26

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

Thessa answered 19/1, 2016 at 21:21 Comment(3)
I started to get this crash when I placed the android:animateLayoutChanges on my RV as well.Advertising
Had this flag set on the parent container (Relative Layout). Fixed the issue.Feleciafeledy
@Feleciafeledy are u saying you had the flag on the parent container and removing it from there fixed the issue?Yakutsk
A
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.

Aureomycin answered 22/3, 2017 at 14:37 Comment(2)
I saw this crash even when I have set the attribute on the parent container of the RV.Yakutsk
@Yakutsk Please post your code and maybe we can look at it?Aureomycin
M
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.

Malaria answered 16/9, 2017 at 10:52 Comment(3)
Worked for me. Thanks.Dill
I'm really worry about people adopting this solution. You lose a lot disabling this flag and the bug is still in other place –Jeffersonjeffery
tbh, it just made all my rows disappear. I must admit though, the crash disappeared as well XDShuler
A
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.

Ahmed answered 5/11, 2018 at 14:12 Comment(1)
But what if i want that transition?Scaffolding
T
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

Thanet answered 11/12, 2015 at 7:24 Comment(3)
Welcome to StackOverflow. Could you please provide a full answer instead of just a link?Indisposition
what is item here?Casemate
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.Thanet
C
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.

Catnap answered 25/11, 2016 at 7:10 Comment(0)
S
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.

Sastruga answered 15/3, 2019 at 1:42 Comment(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.Plucky
H
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);

Hargrave answered 29/11, 2017 at 22:39 Comment(0)
C
5

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

Carden answered 11/9, 2018 at 11:55 Comment(2)
Please note that this question is from 2014 and the properties might have changed by now.Isbell
No its not changed yetPredict
S
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.

Spindlelegs answered 2/5, 2018 at 13:22 Comment(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?Yarndyed
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-layoutSpindlelegs
I
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.

Ichthyo answered 13/1, 2017 at 19:8 Comment(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.Haun
S
2

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

(credits to my workmate for noticing this)

Shuler answered 1/12, 2020 at 14:3 Comment(0)
W
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.

Withe answered 4/9, 2015 at 6:28 Comment(0)
T
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);
        }
    }
Tempting answered 3/3, 2017 at 5:58 Comment(0)
T
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)

Tempting answered 3/3, 2017 at 6:8 Comment(1)
i've done notifyItemRemoved when removing footer and app crashes, change it to notifyDataSetChanged and now it works ok. thank youGoodygoody
R
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.

Realist answered 31/5, 2017 at 1:56 Comment(0)
A
1

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

Alicia answered 11/10, 2017 at 10:15 Comment(0)
F
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.

Fourfold answered 24/10, 2018 at 4:53 Comment(0)
G
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
Gipon answered 20/2, 2015 at 15:58 Comment(0)
C
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);
Cavefish answered 21/1, 2016 at 5:21 Comment(0)
A
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.

Anethole answered 15/3, 2016 at 2:37 Comment(0)
C
0

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

Ceroplastics answered 5/8, 2016 at 12:33 Comment(0)
A
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.

Ahasuerus answered 9/3, 2017 at 14:16 Comment(0)
R
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;
}
Ramonramona answered 15/6, 2017 at 21:29 Comment(0)
B
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);
Brazenfaced answered 25/9, 2017 at 8:50 Comment(0)
B
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.

Burma answered 16/12, 2017 at 3:19 Comment(1)
Hi @evin, how would be the right way to declare this nameLP2? Why is it wrong?Vocabulary

© 2022 - 2024 — McMap. All rights reserved.