RecyclerView crashes when "scrapped or attached views may not be recycled"
AndroidAndroid RecyclerviewAndroid Problem Overview
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 TextView
s, so I won't bother reposting my code.
Anyone else getting this error and know how to deal with it?
Android Solutions
Solution 1 - Android
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.
Solution 2 - Android
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 EditText
s in them. After a while we pinned the issue to being focus related. This bug happens while recycling EditText
s 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.
Solution 3 - Android
I removed the android:animateLayoutChanges
from layout property and problem has been resolved.
Solution 4 - Android
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.
Solution 5 - Android
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.
Solution 6 - Android
I also encountered the same errror when scrolling on the RecyclerView
: then I removed animateLayoutChanges="true"
in layout file for RecyclerView
then everything worked.
Solution 7 - Android
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
Solution 8 - Android
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.
Solution 9 - Android
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.
-
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. -
android:animateLayoutChanges="true"
orTransitionManager.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 therecyclerview
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.
Solution 10 - Android
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);
Solution 11 - Android
I too was getting this error whenever i had animateLayoutChanges="true" in layout file for RecyclerView. Delete this attribute and error will disappear!
Solution 12 - Android
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 the
animateOnLayoutChange' 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.
Solution 13 - Android
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.
Solution 14 - Android
beware not to denote ViewHolder
as data class
if you're using kotlin.
(credits to my workmate for noticing this)
Solution 15 - Android
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.
Solution 16 - Android
/**
* 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);
}
}
Solution 17 - Android
1、remove
:remove data from list.
2、notifyDataSetChanged
:notifyDataSetChanged();
3、notifyItemRemoved
:show animation.
4、notifyItemRangeChanged
:range view size and redraw the viewHolders(onBindViewHolder methods)
Solution 18 - Android
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.
Solution 19 - Android
Remove android:animateLayoutChanges="true"
from recycleview or set android:animateLayoutChanges="false"
Solution 20 - Android
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.
Solution 21 - Android
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
Solution 22 - Android
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);
Solution 23 - Android
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.
Solution 24 - Android
For me the same bug with caused by a LayoutTransition on a higher level ViewGroup.
Solution 25 - Android
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.
Solution 26 - Android
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;
}
Solution 27 - Android
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);
Solution 28 - Android
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.