RecycleView 局部刷新抽风闪烁

1,895 阅读2分钟

RecycleView 局部刷新抽风闪烁

最近使用RecycleView进行列表渲染的时候出现了一些状况

image-20241228181239032

如图,在之前我几乎都是让recycleView全部刷新,但是这个黄线非常让人不爽,我本身也知道这样写对渲染性能有损耗,于是准备notifyItemChanged( position : Int)方法稍稍优化一下(主要是需要传递position,所以之前嫌麻烦就一直没改)

后面改成了这样:

NetApi.getFriendMomentList(activity.app.user.id,
                success = { data: List<MomentResponse> ->
                    this.data = data
                    AppExecutor.mainHandler()
                        .post { this.notifyItemChanged(position) }
                },
                fail = { msg: String ->
                    AppExecutor.mainHandler().post {
                        ToastUtil.showToast(
                            activity, msg
                        )
                    }
                })

然后,非常抽象的事情就来了,调用这个方法进行局部item刷新会有动画显示( RecycleView自带的 ),这个动画会让整个item闪一下

Android-Emulator---Pixel_7_Pro_API_35_5554-2024-12-28-18-21-54

看到没,发送评论后画面闪了一下,GIF帧率低不明显,模拟器上很明显的!!!

可我想要实现的效果是某个item中的view单独刷新,不是整个item,去网上搜索方法,看到大佬建议使用下面这个方法

public final void notifyItemChanged(int position, @Nullable Object payload) {
     mObservable.notifyItemRangeChanged(position, 1, payload);
}

和之前相比多了一个payload参数,是用来配合

public void onBindViewHolder(@NonNull VH holder, int position,@NonNull List<Object> payloads) {}

这个方法一起使用的,这个和自定义RecycleViewAdapter中需要重写的override fun onBindViewHolder(holder: MomentItemViewHolder, position: Int)方法相比多了一个payloads,类型是List,使用也很简单

override fun onBindViewHolder( holder: MomentItemViewHolder, position: Int, payloads: MutableList<Any>) {
        if (payloads.isEmpty()) {
            onBindViewHolder(holder, position)
        } else {
            for (payload in payloads) {
                when (payload as String) {
                    "comment" -> initCommentView(data[position], holder.binding)
                    "like" -> initLikeView(data[position], holder.binding)
                }
            }
        }
    }

如上,在带payloadsonBindViewHolder进行自定义渲染,记得如果payloads为空就调用onBindViewHolder(holder, position)去渲染>,不然你就会得到一个没有渲染view的item,原因也很好理解,看看RecycleView的源码就知道了

public void onBindViewHolder(@NonNull VH holder, int position,@NonNull List<Object> payloads) {
     onBindViewHolder(holder, position);
}

默认不重写onBindViewHolder(@NonNull VH holder, int position,@NonNull List<Object> payloads)方法,方法体中调用的就还是onBindViewHolder(holder, position),如果重写后不在合适的时机调用这个方法,就不会走到我们写RecycleViewAdapter中的重写的那个onBindViewHolder(holder, position),这样的话我们在重写的方法中所进行的数据渲染就没有执行,你就喜提空白数据view

按照上面的方法

NetApi.getFriendMomentList(
    activity.app.user.id,
    success = { data: List<MomentResponse> ->
        this.data = data
        AppExecutor.mainHandler()
            .post { this.notifyItemChanged(position,"comment") }
    },
    fail = { msg: String ->
        AppExecutor.mainHandler().post {
            ToastUtil.showToast(
                activity, msg
            )
        }
    })
NetApi.getLikeList(
	moment.id, 
	success = { data: List<MomentLike> ->
          moment.momentLikes = data as ArrayList<MomentLike>
          AppExecutor.mainHandler().post { this.notifyItemChanged(position, "like") }
    }, 
    fail = { msg ->
          AppExecutor.mainHandler().post {
              ToastUtil.showToast(activity, msg)
          }
})

在需要触发局部刷新的地方调用,并写上payload,标记需要局部刷新的区域

override fun onBindViewHolder( holder: MomentItemViewHolder, position: Int, payloads: MutableList<Any>) {
        if (payloads.isEmpty()) {
            onBindViewHolder(holder, position)
        } else {
            for (payload in payloads) {
                when (payload as String) {
                    "comment" -> initCommentView(data[position], holder.binding)
                    "like" -> initLikeView(data[position], holder.binding)
                }
            }
        }
    }

在重写的onBindViewHolder( holder: MomentItemViewHolder, position: Int, payloads: MutableList<Any>)方法中进行单独处理就好了

看看效果:

Android-Emulator---Pixel_7_Pro_API_35_5554-2024-12-28-19-00-41

要的就是这个效果