当使用Runnable从Handler使用notifyItemChanged时,RecyclerView崩溃[IllegalArgumentException]

Amr*_*dri 24 android recycler-adapter android-recyclerview

RecyclerView用作列表来显示可以下载的歌曲.每个项目都有ProgressBarView.当下载开始时,我使用a Handler来通知每个项目更新ProgressBar以显示歌曲下载进度.

Q1.这是一种正确的方法吗?还是有其他方法可以更恰当地做到这一点.

Q2.当我们adapter.notifyItemChanged(position);用来更新单个项目的内容时,RecyclerView会崩溃.它是从Handler使用中调用的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)
Run Code Online (Sandbox Code Playgroud)

我搜索找到了解决方案但找不到合适的答案.

ali*_*awi 13

我有同样的问题,我可以通过禁用 recyclerview 的动画来处理它:

recyclerView.itemAnimator = null


Hen*_*usa 12

我们的应用程序也遇到了这个问题,经过一段长的调试后我们发现它是由它引起的adapter.setHasStableIds(true)

我们删除了违规行,问题终于消失了.

希望能帮助到你.

  • 解决了我的问题,尽管 getItemId() 为数据项返回了正确的唯一 ID。 (2认同)

Lev*_*yan 10

对我来说,animateLayoutChanges=“true”从 recyclerview 的父 ViewGroup 中删除后,问题得到了解决。


Sir*_*lot 6

我遇到了同样的问题,但结果发现原因是打电话scrollToPosition。经过多次尝试后,对我有用的解决方案是在滚动之前调用notifyDataSetChanged(当然全部来自 UI 线程)。也许它会对某人有所帮助。


and*_*per 6

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

无论如何,出于某种原因,在我的情况下,用 LinearLayout 替换 ConstraintLayout 作为 RecyclerView 的父级已经修复了它。

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

                   if (recyclerView.isAnimating)
                        Handler().post {
                            adapter.items = it.listItems
                            adapter.notifyDataSetChanged()
                        }
                    else {
                         adapter.items = it.listItems
                         adapter.notifyDataSetChanged()
                         }
Run Code Online (Sandbox Code Playgroud)

出于某种原因,这个替代方案在 POC 上对我不起作用,所以我建议改用 LinearLayout 解决方案,或者在您清除数据或像我一样切换到另一个视图时使用它:

                val itemAnimator = recyclerView.itemAnimator
                recyclerView.itemAnimator = null
                clearItems()
                recyclerView.itemAnimator = itemAnimator
Run Code Online (Sandbox Code Playgroud)
    fun clearItems() {
        items.clear()
        recyclerView.adapter!!.notifyDataSetChanged()
        viewSwitcher.setViewToSwitchTo(R.id.emptyView)
    }
Run Code Online (Sandbox Code Playgroud)

在这里向 Google 报告了这个问题。POC 和解决方法(对我有用)也在那里可用。

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


Ole*_*Ole 5

由于这是第一篇文章,我将发布另一个解决方案来解决这个问题,这对我的具体情况有帮助:我隐藏了parentview包含recyclerview使用

parentView.setVisibility(View.GONE);

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


flo*_*gny 4

adapter.notifyItemChanged(position);必须从主线程调用

使用带有 mainLooper 的 Handler 代替您的 Handler

    new Handler(Looper.getMainLooper()).post(new Runnable() {
        @Override
        public void run() {
            //HERE
        }
    });
Run Code Online (Sandbox Code Playgroud)