You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

LiveData驱动的RecyclerView删除条目跳转且效率低,最优更新方法是什么?

RecyclerView结合LiveData更新的最优实现方案

你当前实现的核心问题是每次LiveData数据变更时都会重新实例化适配器、重新为RecyclerView绑定适配器,相当于完全重建整个列表控件,因此会触发列表滚动回顶部,同时全量重绘所有条目,性能损耗极高。

推荐使用官方提供的ListAdapter配合DiffUtil.ItemCallback实现增量更新,该方案会自动在后台线程计算新旧列表的差异,仅更新发生变动的条目,不会触发全量重绘,也不会导致列表跳转回顶部,是RecyclerView更新的标准最优实现。


1. 定义条目差异判断规则

首先创建DiffUtil.ItemCallback实现类,用来对比新旧条目的唯一性和内容差异:

val BasketItemDiffCallback = object : DiffUtil.ItemCallback<BasketItem>() {
    // 判断两个条目是否是同一个对象(通常对比唯一ID)
    override fun areItemsTheSame(oldItem: BasketItem, newItem: BasketItem): Boolean {
        return oldItem.id == newItem.id
    }
    // 判断同一个条目的内容是否发生变化
    override fun areContentsTheSame(oldItem: BasketItem, newItem: BasketItem): Boolean {
        return oldItem == newItem
    }
}

2. 改造适配器继承ListAdapter

适配器不需要自己维护数据集,直接继承ListAdapter,内部通过currentList获取当前列表数据即可:

class BasketFragmentAdapter(
    val viewModel: BasketViewModel,
    val context: Context
) : ListAdapter<BasketItem, BasketFragmentAdapter.ViewHolder>(BasketItemDiffCallback) {

    // 正常实现ViewHolder创建和绑定逻辑
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val view = LayoutInflater.from(context).inflate(R.layout.item_basket, parent, false)
        return ViewHolder(view)
    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        val item = currentList[position]
        // 绑定条目数据,删除按钮点击逻辑直接调用viewModel的删除方法即可
        holder.deleteBtn.setOnClickListener {
            viewModel.deleteBasketItem(item.id)
        }
    }

    class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        val deleteBtn: Button = itemView.findViewById(R.id.btn_delete)
    }
}

3. 调整初始化和观察者逻辑

适配器只在页面初始化时实例化一次,后续LiveData更新时只需要提交新列表即可:

// 页面初始化时只初始化一次适配器
private lateinit var adapter: BasketFragmentAdapter

// 在onViewCreated中完成初始化
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    adapter = BasketFragmentAdapter(viewModel, requireContext())
    recycler.adapter = adapter
    // 观察者只需要提交新列表,不需要重建适配器
    viewModel.loadBasketItems().observe(viewLifecycleOwner) {
        adapter.submitList(it)
    }
}

可选补充:手动使用DiffUtil实现

如果你不想使用封装好的ListAdapter,也可以手动计算差异更新,但需要自行处理线程调度,避免大列表计算卡顿主线程:

viewModel.loadBasketItems().observe(viewLifecycleOwner) { newList ->
    val diffResult = DiffUtil.calculateDiff(object : DiffUtil.Callback() {
        override fun getOldListSize() = adapter.dataList.size
        override fun getNewListSize() = newList.size
        override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
            return adapter.dataList[oldItemPosition].id == newList[newItemPosition].id
        }
        override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
            return adapter.dataList[oldItemPosition] == newList[newItemPosition]
        }
    })
    adapter.dataList = newList
    diffResult.dispatchUpdatesTo(adapter)
}

内容的提问来源于stack exchange,提问作者Dragos Ciupe

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.27 11:15:03