我使用RecyclerView
使用StaggeredGridLayoutManager
从Android网站获取的简单实现,我不断收到此错误,导致我的应用崩溃:
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)
简单来说,我的意思是它与this page on their website采用相同的实现,唯一的区别是我的网格项的布局是ImageView
和几个TextView
,所以我不打算重新发布我的代码。
是否有其他人收到此错误并知道如何处理?
答案 0 :(得分:176)
如果您的XML中android:animateLayoutChanges
设置为true并且您在Java代码中的RecyclerView适配器上调用notifyDataSetChanged()
,则会导致此错误。
因此,请避免将android:animateLayoutChanges
与RecyclerViews一起使用。
答案 1 :(得分:47)
我也必须处理此次崩溃,而在我的情况下,它与android:animateLayoutChanges
无关。
我们正在构建的RecyclerView
中包含多种类型的视图,其中一些视图中包含EditText
个视图。过了一会儿,我们把问题固定在与焦点有关的问题上。这个错误发生在回收EditText
时,其中一个是重点。
当然,我们尝试在将新数据绑定到循环播放的视图时清除焦点但在android:focusableInTouchMode="true"
上设置RecycleView
之前不起作用。实际上,这是此问题最终需要消除的唯一变化。
答案 2 :(得分:23)
我从布局属性中删除了android:animateLayoutChanges
,问题已解决。
答案 3 :(得分:14)
在任何人都可以面对此问题的原因中,请检查您是否已将属性android:animateLayoutChanges="true"
设置为RecyclerView。这将导致回收和重新附加RecyclerView的项目失败。删除它并将属性分配给RecyclerView的父容器,例如LinearLayout / RelativeLayout,您应该会看到问题消失。
答案 4 :(得分:10)
我花了两天但是无法解决这个问题,最后,我不得不禁用项目预取。
设置布局管理器时,您只需调用
即可 mGridLayoutManager.setItemPrefetchEnabled(false);
这让我的错误消失了。希望它对某人有用。
答案 5 :(得分:8)
使用slimfit粘贴标题时遇到此错误。这是因为错误的第一个位置造成的。我得到了答案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);
}
确保为mSectionFirstPosition
传递正确的值答案 6 :(得分:7)
今天早上我遇到了这个问题,但我没有遇到与上述相同的原因。
通过调试我发现我的ViewHolder中的项目视图有mParent
并且它不是空的,在正常情况下它应该是无(这就是日志所说的'&# 34;附加视图可能无法回收",我认为这意味着如果子视图已经附加到父级,那么在回收时会有一些原因导致失败。)
但是我每次手动都没有附加子视图。当我尝试在ViewHolder中对子视图进行充气时,我发现它已经完成了,例如:
layoutInflater.inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot)
最后一个参数attachToRoot
应为false。
我将其更改为false
后,我解决了问题。
顺便说一句,我只看到当我将支持库升级到最新版本25.0.0时发生此崩溃。在我使用版本23.4.0之前,我没有看到这个问题发生。我想最新的支持库应该有一些变化。
希望得到这个帮助。
答案 7 :(得分:6)
在我的情况下,它发生的原因是我在尝试调整RecyclerView的大小时运行了Transition
,因为软件键盘即将显示。
我使用Transition
Transition.excludeTarget(R.id.recyclerview, true);
中的RecyclerView
答案 8 :(得分:5)
在RecyclerView
上滚动时,我也遇到了同样的错误:然后我在布局文件中删除了animateLayoutChanges="true"
的{{1}},然后一切正常。
答案 9 :(得分:5)
每当我在RecyclerView的布局文件中有animateLayoutChanges =“ true”时,我也都会收到此错误。 删除该属性,错误将消失!
答案 10 :(得分:4)
虽然在我的情况下它正在从修复崩溃的recyclerView中删除animateOnLayoutChange
,但我仍然需要能够在viewHolder中设置布局更改的动画。为了实现这一点,LinearLayout' in the view holder needs the
animateOnLayoutChange'要真实,但我需要notifyItemChanged
到适配器。然后,这允许两个layoutTransition动画启动(用于展开和折叠viewHolder),并且还避免了报废的异常。所以,是的,避免将animateOnLayoutChange放在recylcerView上,并使用各种notify方法启用视图大小更改的默认动画。
答案 11 :(得分:3)
我通过删除parent.addView()
onCreateViewHolder
来解决此问题
这是我的代码
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()
处的功能检查我的按钮是否已经是父母。如果我们向父级添加按钮,则会将RecyclerView
分配给其mParent
变量。
答案 12 :(得分:2)
我通过调用
解决了这个问题setHasStableIds(true);
在适配器的构造函数中并覆盖适配器中的getItemId
:
@Override
public long getItemId(int position) {
return position;
}
答案 13 :(得分:1)
当我在ViewHolder
中为RecyclerView
适配器使用自定义对象时,我发现这种情况发生了。
为了解决这个问题,我清除了自定义对象,在我的情况下是适配器onViewRecycled(ViewHolder holder)
中的一个计时器,如下所示:
public void onViewRecycled(ViewHolder holder) {
if(holder instanceof EntityViewHolder) {
if(((EntityViewHolder)holder).timer != null) {
((EntityViewHolder) holder).timer.cancel();
}
}
super.onViewRecycled(holder);
}
这解决了这个问题。
答案 14 :(得分:1)
此异常不是
的原因机器人:animateLayoutChanges
或
机器人:focusableInTouchMode
这个最终正确的答案只是因为你设置了错误的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);
nameLP没问题。 名称LP2发生崩溃.bug就在这里。
我尝试了本页面的所有答案。相信我。
答案 15 :(得分:1)
从recycleview中删除android:animateLayoutChanges="true"
或设置android:animateLayoutChanges="false"
答案 16 :(得分:1)
就我而言,我在使用recyclerView顶部添加视图之前使用了TransitionManager.beginDelayedTransition()
。我删除了TransitionManager.beginDelayedTransition()
并且没有崩溃。
答案 17 :(得分:1)
1,remove
:从列表中删除数据。
2,notifyDataSetChanged
:notifyDataSetChanged();
3,notifyItemRemoved
:显示动画。
4,notifyItemRangeChanged
:范围视图大小并重绘viewHolders(onBindViewHolder methods)
答案 18 :(得分: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);
}
}
答案 19 :(得分:0)
我遇到的一个特殊情况是,我在适配器中有一个视图成员,我懒得实例化一个不需要使用循环视图的视图。
这也违反了回收视图原则,在这种情况下,您可以存储对视图的引用。我在下面给出一个简单的例子:
// 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
答案 20 :(得分:0)
解决方法解决方案如果Exception的原因是itemView具有父级。 在代码中,您有notifyItemRemoved(position),从RecyclerView中删除itemView:
View itemView = mRecyclerView.getLayoutManager().findViewByPosition(position);
if (itemView != null && itemView.getParent() != null) {
((ViewGroup) itemView.getParent()).removeView(itemView);
}
notifyItemRemoved(position);
答案 21 :(得分:0)
在我的情况下,问题是因为此方法public long getItemId(int position)
的执行不正确(从RecyclerView.Adapter
方法覆盖)。
旧代码将为同一个项目获取两个不同的ID(在我的情况下,它是页脚项目),在修复实现后问题已经消失。
答案 22 :(得分:0)
对我来说,在更高级别的ViewGroup上由LayoutTransition引起的同样的错误。
答案 23 :(得分:0)
请允许我为此类问题添加另一种可能的解决方法。对于RecyclerView
中的粘性标题,superSlim库存在同样的问题。我使用MatrixCursor
将数据设置为RecyclerViewCursorAdapter
。出现此问题的原因是所有标头的ID列等于0
。希望这可以帮助某人节省几天的调试时间。
答案 24 :(得分:0)
我遇到了这个问题,因为我通过计算数据相等性和哈希码覆盖了equals()
的{{1}}的{{1}}的{{1}}和hashcode()
方法,因此回收逻辑不起作用并崩溃了,我只删除了覆盖并修复了。
答案 25 :(得分:0)
我使用com.squareup.picasso.RequestCreator
public void into(android.widget.ImageView target,
Callback callback)
从Internet下载图片后动态调整ImageView的大小,并保存调整大小的宽度和高度以保留视图大小。我得到了这个例外,因为我将LayoutParams
保存在Map
中,在我的onBindViewHolder中,我检索了它并直接将其设置为我的ImageView
。我通过使用ImmutablePair<Integer, Integer>
来解决这个问题,只存储ImageView的大小而不是很多其他状态,并使用以下代码来恢复它。
ViewGroup.LayoutParams params = image.getLayoutParams();
params.width = widthAndHeight.getLeft();
params.height = widthAndHeight.getRight();
image.setLayoutParams(params);
答案 26 :(得分:0)
调用此异常有很多原因。在我的情况下,这是由于运行动画,这就是为什么视图仍附加且无法删除到视图的原因。只有在动画制作完成后,视图才能被删除和回收。
有两种类型的动画可能会影响recyclerview的回收。
1)是RecyclerView.ItemAnimator
-这应该不是问题。使用它应该非常安全,因为它可以检查附加视图和已废弃视图并正确处理回收。
2)android:animateLayoutChanges="true"
或TransitionManager.beginDelayedTransition()
或TransitionManager.go(),等等。-这些动画独立运行并抓住要进行动画处理的项目。这导致视图被强制附加到动画完成为止。 recyclerview不了解这些动画,因为它不在其范围内。因此recyclerview
可能会尝试回收某个项目,认为它可以正确回收,但是问题是这些API仍会保留视图,直到动画结束为止。
如果您使用的是android:animateLayoutChanges="true"
或TransitionManager.beginDelayedTransition()
或TransitionManager.go()等,只需从动画中删除RecyclerView
及其子元素即可。
您只需按住Transition
并致电
Transition.excludeChildren(yourRecyclerView, true)
Transition.excludeTarget(yourRecyclerView, true)
注意:
请注意,重要的是使用Transition.excludeChildren()
从动画中排除所有Recyclerview
个孩子,而不仅仅是Recyclerview
本身。
答案 27 :(得分:0)
请注意,如果您使用的是Kotlin,请不要将np.stack()
表示为ViewHolder
。
(感谢我的同事注意这一点)