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?

Vasily Kabunov
  • 6,511
  • 13
  • 49
  • 53
StackOverflowMaster
  • 3,478
  • 3
  • 18
  • 19

28 Answers28

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.

StackOverflowMaster
  • 3,478
  • 3
  • 18
  • 19
  • 22
    then how one can use animateLayoutChanges feature in recyclerview? – Dhrumil Shah - dhuma1981 Dec 24 '14 at 11:28
  • What are you trying to achieve? Item animation? If so, the RecyclerView API supports this - take a look at the documentation: https://developer.android.com/reference/android/support/v7/widget/RecyclerView.html – Kenneth Feb 09 '15 at 08: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 01 '15 at 16:39
  • `RecyclerView` uses `DefaultItemAnimator` by default. – Jade Apr 26 '16 at 20:23
  • -,- i have this problem exactly how u described it – Ninja Coding Aug 17 '16 at 16:23
  • Couldn't find a way to animate column removal in RecyclerView with GridLayoutManager, thought to use animateLayoutChanges but also got this crash... – frangulyan Nov 06 '16 at 23:08
  • Please see my answer to set animateLayoutChanges with RecyclerViews and still avoid the crash. – Ram Iyer Oct 26 '17 at 12:29
  • 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. – Mr-IDE Nov 27 '19 at 18:00
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.

Charuක
  • 12,953
  • 5
  • 50
  • 88
Nemanja Kovacevic
  • 3,510
  • 2
  • 30
  • 45
  • 2
    Fantastic, solved multiple focus related issues I had when using EditTexts in a RecyclerView. Thanks! – Rabie Jradi Aug 25 '15 at 13:06
  • 1
    I had ACET in recyclerview, and it crushes. This post saved me. – Kai Wang Mar 01 '16 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 '16 at 07: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 '20 at 05:59
26

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

Derlin
  • 9,572
  • 2
  • 32
  • 53
Özer Özcan
  • 1,253
  • 16
  • 17
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.

Ram Iyer
  • 1,621
  • 1
  • 23
  • 25
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.

Atiq
  • 14,435
  • 6
  • 54
  • 69
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.

PMerlet
  • 2,568
  • 4
  • 23
  • 39
user8796389
  • 91
  • 1
  • 1
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

muetzenflo
  • 5,653
  • 4
  • 41
  • 82
Jaspinder Kaur
  • 1,129
  • 11
  • 11
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.

Anthonyeef
  • 2,595
  • 1
  • 27
  • 25
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.

Archie G. Quiñones
  • 11,638
  • 18
  • 65
  • 107
  • 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`. – Danilo Prado Apr 03 '20 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);

Tunji_D
  • 3,677
  • 3
  • 27
  • 35
5

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

rvd
  • 109
  • 1
  • 3
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.

kingargyle
  • 1,239
  • 10
  • 15
  • 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 02 '19 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. https://github.com/chuross/expandable-layout – kingargyle Feb 04 '19 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.

egon12
  • 780
  • 5
  • 22
  • 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. – Kirill Kulakov Jun 08 '17 at 08:20
2

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

(credits to my workmate for noticing this)

Antek
  • 721
  • 1
  • 4
  • 27
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.

Vasily Kabunov
  • 6,511
  • 13
  • 49
  • 53
Androidrp
  • 11
  • 1
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);
        }
    }
ZhangTengyuan
  • 1,295
  • 1
  • 8
  • 8
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)

ZhangTengyuan
  • 1,295
  • 1
  • 8
  • 8
  • i've done `notifyItemRemoved` when removing `footer` and app crashes, change it to `notifyDataSetChanged` and now it works ok. thank you – Siarhei Jun 04 '17 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.

Hai nguyen thanh
  • 718
  • 7
  • 17
1

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

Rajesh N
  • 6,198
  • 2
  • 47
  • 58
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.

Pankaj Lilan
  • 4,245
  • 1
  • 29
  • 48
Irwin
  • 11
  • 1
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
Derlin
  • 9,572
  • 2
  • 32
  • 53
auracool
  • 172
  • 8
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);
cmicat
  • 347
  • 3
  • 5
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.

Vasily Kabunov
  • 6,511
  • 13
  • 49
  • 53
MistaGreen
  • 798
  • 1
  • 7
  • 15
0

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

mattlaabs
  • 486
  • 3
  • 14
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.

Mu Sa
  • 331
  • 3
  • 8
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;
}
Kilian Obermeier
  • 6,678
  • 4
  • 38
  • 50
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);
Polurival
  • 58
  • 1
  • 9
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.

evin
  • 17
  • 2