android-recyclerview - 使用 LiveData、Room 和 DiffUtil 更新 RecyclerView 项目内的内部 View

标签 android-recyclerview android-room android-adapter android-livedata android-diffutils

我在 Room 中有消息表,其结构如下:id、text、animRes、readStatus。

然后我订阅 room 公开的 livedata,以使用 diffutil 适配器在 recyclerview 中显示该项目。

当 RV 中出现新消息时,动画开始播放。如果我将 readStatus 添加到 diffutil areContentsTheSame 函数,则每次 readStatus 更改时我的动画都会重新启动,这就是为什么我不能只使用 diffutil 重绘完整 View 。

当房间中的值(value)发生变化时,是否有任何方法可以仅更新 RV 项目内的一个内部指示器 View ? 这在 Messenger 中如何工作,更改阅读状态不会重新启动动画(贴纸或 gif)?

谢谢)

最佳答案

终于找到答案了)

实现 DiffUtil(如果您想要部分更新,getChangePayload 应该返回一些内容,否则返回 null 以进行完全重新绑定(bind)):

    class DiffUtilCallback : DiffUtil.ItemCallback<MessageModel>() {
        override fun getChangePayload(oldItem: MessageModel, newItem: MessageModel): Any? {
            if (oldItem.readStatus!=newItem.readStatus) {
                return Bundle().apply {
                    putInt("readStatus", newItem.readStatus)
                }
            }
            return null
        }
        override fun areItemsTheSame(oldItem: MessageModel, newItem: MessageModel): Boolean {
            return oldItem == newItem
        }

        override fun areContentsTheSame(oldItem: MessageModel, other: MessageModel): Boolean {
            oldItem.apply {
                return id == other.id && text == other.text && animRes == other.animRes && readStatus == other.readStatus
            }
        }
    }

然后在适配器中(此处的 ViewBinding 示例: https://stackoverflow.com/a/60427658 ):

    class MessageAdapter : ListAdapter<MessageModel, MessageViewHolder>(DiffUtilCallback()) {

        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MessageViewHolder {
                val binding = InboundMessageBinding.inflate(LayoutInflater.from(parent.context), parent, false)
                return MessageViewHolder(binding)
        }

        override fun onBindViewHolder(holder: MessageViewHolder, position: Int) {
            holder.bind(getItem(position))
        }

        override fun onBindViewHolder(holder: MessageViewHolder, position: Int, payloads: MutableList<Any>) {
            if (payloads.isNotEmpty()) {
                val item = payloads[0] as Bundle
                val readStatus = item.getInt("readStatus")
                holder.binding.status.text = readStatus.toString()
                return
            }
            super.onBindViewHolder(holder, position, payloads)
        }
    }

仅当 diffUtil 的 getChangePayload 返回 null 时,我们才会调用 onBindViewHolder 构造函数。否则只需更改目标 View ,简单有效)

更新:如果在添加动画播放时负载发生变化,两个动画会相互重叠,结果不太好。

可以通过以下方式解决此问题:

    messageRv.itemAnimator = object : DefaultItemAnimator()  {
        override fun animateChange(oldHolder: RecyclerView.ViewHolder?, newHolder: RecyclerView.ViewHolder?, fromX: Int, fromY: Int, toX: Int, toY: Int): Boolean {
            if ((oldHolder as? MessageViewHolder)?.itemId==(newHolder as? MessageViewHolder)?.itemId) { //don't animate same view to prevent blinking when only readStatus changes
                dispatchChangeFinished(newHolder, true) //for correct next animation
                return true
            }
            return super.animateChange(oldHolder, newHolder, fromX, fromY, toX, toY)
        }
    }

关于android-recyclerview - 使用 LiveData、Room 和 DiffUtil 更新 RecyclerView 项目内的内部 View ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67238020/

相关文章:

java - 从主 Activity 调用方法到 fragment 适配器

java - Recyclerview 适配器中的空指针异常

android - 当卡不在视野中时,FindViewHolderForPosition 返回 null

java - 如何在RecyclerView的分页器中设置正确的点偏移

android - Room SQLite 文件 - 确保刷新所有更改并且在文件上传过程中没有 I/O

android - 找不到字段的 setter - 将 Kotlin 与 Room 数据库结合使用

android - 显示来自 firebase 数据库的消息

java - RecyclerView 没有 LayoutManager android.support.v7.widget.RecyclerView

android - 如何创建将我的对象存储在 Room 中的 Intent ?

android - 有没有办法将两个 TextView 添加到 arrayadapter