Android 当使用Runnable从处理程序更改use NotifyItem时,RecyclerView崩溃[IllegalArgumentException]

Android 当使用Runnable从处理程序更改use NotifyItem时,RecyclerView崩溃[IllegalArgumentException],android,android-recyclerview,Android,Android Recyclerview,我正在使用RecyclerView作为列表来显示可下载的歌曲。每个项目在其视图中都有ProgressBar。下载开始时,我使用处理程序通知每个项目更新进度条,以显示歌曲下载进度 Q1。这是正确的方法还是有其他更合适的方法。 Q2。当我们使用adapter.notifyItemChanged(位置)时,RecyclerView崩溃更新单个项目的内容。使用Runnable从处理程序调用它。但是,日志没有显示我的代码的任何跟踪。为什么? 以下是此问题的日志: 05-06 19:09:45.804: E

我正在使用
RecyclerView
作为列表来显示可下载的歌曲。每个项目在其
视图中都有
ProgressBar
。下载开始时,我使用
处理程序
通知每个项目更新
进度条
,以显示歌曲下载进度

Q1。这是正确的方法还是有其他更合适的方法。

Q2。当我们使用
adapter.notifyItemChanged(位置)时,RecyclerView崩溃
更新单个项目的内容。使用
Runnable
处理程序调用它。但是,日志没有显示我的代码的任何跟踪。为什么?

以下是此问题的日志:

05-06 19:09:45.804: E/AndroidRuntime(32115): FATAL EXCEPTION: main
05-06 19:09:45.804: E/AndroidRuntime(32115): java.lang.IllegalArgumentException: Tmp detached view should be removed from RecyclerView before it can be recycled: ViewHolder{41b7bec0 position=6 id=-1, oldPos=-1, pLpos:-1 update changed tmpDetachedundefined adapter position no parent}
05-06 19:09:45.804: E/AndroidRuntime(32115):    at android.support.v7.widget.RecyclerView$Recycler.recycleViewHolderInternal(RecyclerView.java:3861)
05-06 19:09:45.804: E/AndroidRuntime(32115):    at android.support.v7.widget.RecyclerView.removeAnimatingView(RecyclerView.java:779)
05-06 19:09:45.804: E/AndroidRuntime(32115):    at android.support.v7.widget.RecyclerView.access$5300(RecyclerView.java:127)
05-06 19:09:45.804: E/AndroidRuntime(32115):    at android.support.v7.widget.RecyclerView$ItemAnimatorRestoreListener.onAddFinished(RecyclerView.java:8228)
05-06 19:09:45.804: E/AndroidRuntime(32115):    at android.support.v7.widget.RecyclerView$ItemAnimator.dispatchAddFinished(RecyclerView.java:8573)
05-06 19:09:45.804: E/AndroidRuntime(32115):    at android.support.v7.widget.DefaultItemAnimator$5.onAnimationEnd(DefaultItemAnimator.java:239)
05-06 19:09:45.804: E/AndroidRuntime(32115):    at android.support.v4.view.ViewPropertyAnimatorCompatJB$1.onAnimationEnd(ViewPropertyAnimatorCompatJB.java:47)
05-06 19:09:45.804: E/AndroidRuntime(32115):    at android.view.ViewPropertyAnimator$AnimatorEventListener.onAnimationEnd(ViewPropertyAnimator.java:973)
05-06 19:09:45.804: E/AndroidRuntime(32115):    at android.animation.ValueAnimator.endAnimation(ValueAnimator.java:1012)
05-06 19:09:45.804: E/AndroidRuntime(32115):    at android.animation.ValueAnimator.access$400(ValueAnimator.java:51)
05-06 19:09:45.804: E/AndroidRuntime(32115):    at android.animation.ValueAnimator$AnimationHandler.doAnimationFrame(ValueAnimator.java:623)
05-06 19:09:45.804: E/AndroidRuntime(32115):    at android.animation.ValueAnimator$AnimationHandler.run(ValueAnimator.java:639)
05-06 19:09:45.804: E/AndroidRuntime(32115):    at android.view.Choreographer$CallbackRecord.run(Choreographer.java:776)
05-06 19:09:45.804: E/AndroidRuntime(32115):    at android.view.Choreographer.doCallbacks(Choreographer.java:579)
05-06 19:09:45.804: E/AndroidRuntime(32115):    at android.view.Choreographer.doFrame(Choreographer.java:547)
05-06 19:09:45.804: E/AndroidRuntime(32115):    at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:762)
05-06 19:09:45.804: E/AndroidRuntime(32115):    at android.os.Handler.handleCallback(Handler.java:725)
05-06 19:09:45.804: E/AndroidRuntime(32115):    at android.os.Handler.dispatchMessage(Handler.java:92)
05-06 19:09:45.804: E/AndroidRuntime(32115):    at android.os.Looper.loop(Looper.java:153)
05-06 19:09:45.804: E/AndroidRuntime(32115):    at android.app.ActivityThread.main(ActivityThread.java:5297)
05-06 19:09:45.804: E/AndroidRuntime(32115):    at java.lang.reflect.Method.invokeNative(Native Method)
05-06 19:09:45.804: E/AndroidRuntime(32115):    at java.lang.reflect.Method.invoke(Method.java:511)
05-06 19:09:45.804: E/AndroidRuntime(32115):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:833)
05-06 19:09:45.804: E/AndroidRuntime(32115):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:600)
05-06 19:09:45.804: E/AndroidRuntime(32115):    at dalvik.system.NativeStart.main(Native Method)

我搜索了此问题的解决方案,但找不到任何合适的答案。

adapter.notifyItemChanged(位置)

使用带有mainLooper的处理程序代替处理程序

    new Handler(Looper.getMainLooper()).post(new Runnable() {
        @Override
        public void run() {
            //HERE
        }
    });

我们的应用程序上也有这个问题,经过很长时间的调试,我们发现它是由
adapter.setHasStableIds(true)

我们去掉了冒犯的界线,问题终于解决了


希望能有所帮助。

有几种解决方法:

  • 调用
    notifyDataSetChanged()
    而不是
    notifyItemChanged()
    。这是解决这个问题效率较低的方法

  • 正如Henrique de Sousa所指出的那样,删除线
    适配器。setHasStableIds(true)
    可防止出现问题


  • 但是,如果适配器是稳定的,那么真正的解决方案(正如southerton所评论的那样)是正确地实现
    getItemId()
    ,尊重“HassTableId”值集。

    我遇到了同样的问题,但结果证明原因是调用
    scrollToPosition
    。在多次尝试之后,我的解决方案是在滚动之前调用
    notifydatasetchange
    (当然,所有这些都是从UI线程中获得的)。也许它会对某人有所帮助。

    因为这是第一篇文章,所以我将发布另一个解决问题的方法,这对我的具体情况有所帮助: 我正在使用隐藏包含
    recyclerview
    parentview

    
    parentView.setVisibility(View.GONE);
    


    recyclerview不喜欢这样,显然,如果要隐藏包含recyclerview的视图,则只能使用
    view.INVISIBLE

    我也遇到了同样的问题,我可以通过禁用recyclerview的animator来解决:


    recyclerView.itemAnimator=null对我来说,在我从recyclerView的父视图组中删除了
    animateLayoutChanges=“true”
    后,问题得到了解决。

    这里的一些人说“android:animateLayoutChanges”不应该设置为true,但我没有设置,设置为false也没有帮助

    无论如何,出于某种原因,在我的例子中,将ConstraintLayout替换为LinearLayout作为RecyclerView的父级已经修复了它

    另一种选择是,在我让RecyclerView的适配器对其进行任何更改或对RecyclerView的父级进行任何更改(在我的情况下,我在ViewAnimator中切换视图)之前,我添加了以下检查:

                       if (recyclerView.isAnimating)
                            Handler().post {
                                adapter.items = it.listItems
                                adapter.notifyDataSetChanged()
                            }
                        else {
                             adapter.items = it.listItems
                             adapter.notifyDataSetChanged()
                             }
    
    由于某些原因,此替代方案在POC上不适用于我,因此我建议改用LinearLayout解决方案,或者在清除数据或切换到其他视图时使用此解决方案,就像我所做的那样:

                    val itemAnimator = recyclerView.itemAnimator
                    recyclerView.itemAnimator = null
                    clearItems()
                    recyclerView.itemAnimator = itemAnimator
    
    我向谷歌报告了这个问题。POC和变通方法(对我来说是有效的)也可以在那里找到


    POC显示,如果RecyclerView位于ConstraintLayout内部,则会发生这种情况,ConstraintLayout位于ViewAnimator内部。场景是,当RecyclerView正在为其某些视图设置动画(例如,删除单个项目)时,我会清除其所有项目并切换到ViewAnimator中的另一个视图。

    唯一对我有效的解决方案

        fun RecyclerView.executeAfterAllAnimationsAreFinished(
            callback: (RecyclerView) -> Unit
    ) = post(
            object : Runnable {
                override fun run() {
                    if (isAnimating) {
                        //if isAnimating() returned true itemAnimator will be non-null
                        itemAnimator!!.isRunning {
                            post(this)
                        }
                    } else {
                        callback(this@executeAfterAllAnimationsAreFinished)
                    }
                }
            }
    )
    

    说明:它会一直等到itemAnimator停止设置动画并提供recyclerview回调。

    如果recycler视图父对象是
    ConstraintLayout
    内部
    ViewAnimator/ViewSwitcher
    请尝试:

  • 在回收器视图中设置水平约束
  • 将回收器视图
    layout\u width
    设置为
    0dp

  • 或者,如果recycler视图是直接子视图,则对
    ViewAnimator/ViewSwitcher

    执行相同的操作,如果您在ConstraintLayout中使用recycler视图。可以通过向回收器添加水平约束并将其宽度更改为0dp来解决此问题

    <androidx.recyclerview.widget.RecyclerView
                    android:id="@+id/recyclerView"
                    android:layout_width="0dp"
                    android:layout_height="0dp"
                    app:layout_constraintBottom_toBottomOf="parent"
                    app:layout_constraintLeft_toLeftOf="parent"
                    app:layout_constraintRight_toRightOf="parent"
                    app:layout_constraintTop_toTopOf="parent" />
    
    
    
    我总是在UI线程中处理这些事情来调用它们。这不是我现在面临的问题。这是动画师的问题。有时会抛出,有时不会,我不知道如何修复:(Hi @ AMRUT BITRI,如果这个或任何答案已经解决了你的问题,请通过点击复选标记来考虑。这向更广泛的社区表明,你已经找到了解决方案并给回答者和你自己带来了一些声誉。没有义务这么做。解决了我的问题,尽管GETITEMID()为数据项返回了正确的唯一ID。我这样做:“在一个很长的调试会话之后”:d下载投票者是否可以解释?我认为如果这是一个原因不同的常见问题,那么在线程中评论其他解决方案是很常见的。很好地找到Ole。这是我的问题。我必须使回收器视图不可见(没有消失)-砰的一声,崩溃消失了。这也解决了我的问题,但如果你真的需要设置更改的动画,该怎么办?:/n你还需要恢复它,否?否则,当项目列表更改时,你会丢失漂亮的动画,否?在这个问题上停留了数小时。这为我停止了崩溃。你能为此添加一些上下文吗?我们何时调用此扩展N
    <androidx.recyclerview.widget.RecyclerView
                    android:id="@+id/recyclerView"
                    android:layout_width="0dp"
                    android:layout_height="0dp"
                    app:layout_constraintBottom_toBottomOf="parent"
                    app:layout_constraintLeft_toLeftOf="parent"
                    app:layout_constraintRight_toRightOf="parent"
                    app:layout_constraintTop_toTopOf="parent" />