RecyclerView崩溃时,“报废或附加的意见可能无法回收”
我正在使用StaggeredGridLayoutManager
从Android网站采取一个简单的RecyclerView
实现,我不断收到这个崩溃我的应用程序的错误:
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)
简单地说,我的意思是说,这是从他们的网站上的这个页面采取的相同的实现,唯一的区别是我的网格项目的布局是一个ImageView
和几个TextView
,所以我不会打扰重新发布我的代码。
任何人得到这个错误,并知道如何处理它?
如果在您的XML android:animateLayoutChanges
设置为true,并且您在Java代码中的RecyclerView适配器上调用notifyDataSetChanged()
,则会导致此错误。
所以,只要避免使用android:animateLayoutChanges
与RecyclerViews。
我不得不处理这个崩溃,在我的情况下,它与android:animateLayoutChanges
没有任何android:animateLayoutChanges
。
我们正在构build的RecyclerView
有不止一种types的视图,其中一些有EditText
。 过了一会儿,我们把这个问题固定在焦点上。 这个错误发生在回收EditText
,其中一个焦点。
当然,我们尝试清除新的数据被绑定到一个循环视图的焦点,但是这并不工作,直到在RecycleView
上设置了android:focusableInTouchMode="true"
RecycleView
android:focusableInTouchMode="true"
。 其实这是这个问题最终需要消除的唯一改变。
我从布局属性中删除了android:animateLayoutChanges
,问题已经解决。
当使用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的正确值
任何人都可以面对这个问题的原因之一,检查你是否已经设置属性android:animateLayoutChanges="true"
到RecyclerView。 这将导致回收和重新附加RecyclerView的项目失败。 删除它,并将属性分配给RecyclerView的父容器,如LinearLayout / RelativeLayout,你应该看到问题消失。
今天早上我遇到了这个问题,但是我没有面对上面提到的同样的原因。
通过debugging,我发现我的ViewHolder中的项目视图有mParent
,它不是空的,在正常情况下,它应该是没有的(这就是日志说,“附加视图可能无法回收”,我认为这意味着,如果孩子查看已经附加到父母,这将是一些如何导致回收时失败。)
但是我没有每次手动附加子视图。 我发现,当我尝试膨胀我的ViewHolder中的子视图时,这是完成的,如下所示:
layoutInflater.inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot)
最后一个参数attachToRoot
应该是false。
在我将其更改为false
,我解决了我的问题。
顺便说一句,我只看到这个崩溃发生时,我升级我的支持库到最新版本25.0.0。 在我使用版本23.4.0之前,我没有看到这个问题的发生。 我想在最新的支持库中应该改变一些东西。
希望这个帮助。
我通过删除onCreateViewHolder
parent.addView()
来解决这个问题
这是我的代码
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()
函数检查我的button是否已经是父母或不。 如果我们将button添加到父项,那么也会将RecyclerView
分配给它的mParent
variables。
当我在RecyclerView
适配器的ViewHolder
使用自定义对象时,我看到了这种情况。
为了解决这个问题,我清除了自定义对象,在我的情况是一个计时器在onViewRecycled(ViewHolder holder)
适配器如下:
public void onViewRecycled(ViewHolder holder) { if(holder instanceof EntityViewHolder) { if(((EntityViewHolder)holder).timer != null) { ((EntityViewHolder) holder).timer.cancel(); } } super.onViewRecycled(holder); }
这修正了这个错误。
/** * 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, remove
:从列表中删除数据。
2, notifyDataSetChanged
:notifyDataSetChanged();
3, notifyItemRemoved
:显示animation。
4, notifyItemRangeChanged
:范围视图大小并重绘viewHolders(onBindViewHolder methods)
我通过调用来解决这个问题
setHasStableIds(true);
在适配器的构造函数中,并在适配器中重写getItemId
:
@Override public long getItemId(int position) { return position; }
我使用com.squareup.picasso.RequestCreator
public void into(android.widget.ImageView target, Callback callback)
从Internet下载图片后dynamic调整ImageView的大小,并保存调整后的宽度和高度以保留视图大小。 我得到这个exception,因为我保存在Map
LayoutParams
,并在我的onBindViewHolder,我检索它,并直接将其设置为我的ImageView
。 我通过使用ImmutablePair<Integer, Integer>
来解决这个问题,只存储ImageView的大小,而不是很多其他的状态,并使用下面的代码来恢复它。
ViewGroup.LayoutParams params = image.getLayoutParams(); params.width = widthAndHeight.getLeft(); params.height = widthAndHeight.getRight(); image.setLayoutParams(params);
对于我来说,由更高级ViewGroup上的LayoutTransition引起的同样的错误。
请为此类问题添加另一个可能的修复方法。 我在RecyclerView
遇到了与superSlim库中的粘性头相同的问题。 我使用MatrixCursor
将数据设置为RecyclerViewCursorAdapter
。 这个问题的原因是所有头的ID列等于0
。 希望能帮助别人节省几天的debugging时间。
在我的情况下,这个问题是由于这个方法public long getItemId(int position)
(从RecyclerView.Adapter
方法重写public long getItemId(int position)
的不正确的实现。
旧的代码将得到两个不同的ID为同一项目(在我的情况下,它是页脚项目),修复实施后,问题已经消失。
在我的情况下,我使用了TransitionManager.beginDelayedTransition()
然后在recyclerView顶部添加视图。 我删除了TransitionManager.beginDelayedTransition()
并没有崩溃。
这花了我两天,但无法绕过这个,最后,我不得不禁用项目预取。
设置布局pipe理器时,您可以直接调用
mGridLayoutManager.setItemPrefetchEnabled(false);
这让我的错误消失了。 希望对某人有用。
如果Exception的原因是itemView有父项的解决方法。 在代码中,你有notifyItemRemoved(位置),从RecyclerView中删除itemView:
View itemView = mRecyclerView.getLayoutManager().findViewByPosition(position); if (itemView != null && itemView.getParent() != null) { ((ViewGroup) itemView.getParent()).removeView(itemView); } notifyItemRemoved(position);
从android:animateLayoutChanges="true"
删除android:animateLayoutChanges="true"
,或设置android:animateLayoutChanges="false"
一个奇怪的例子是我在我有一个视图成员在适配器,我懒惰实例化视图,这是不需要做的回收视图。
这也违背了回收视图的原则,在这种情况下存储对视图的引用。 下面给出一个简单的例子:
// 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