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

Android Kotlin:如何不重建Adapter更新RecyclerView传入的数据?

解决RecyclerView数据更新问题(不重建Adapter)

核心问题分析

你之前的问题根源在于:直接在Fragment中修改ViewModel的items集合(同一MutableList引用),StateFlow不会感知到集合内容的变化(因为StateFlow是基于值相等判断是否发射新数据,引用不变的话不会触发发射),所以必须手动调用setRecyclerView,但这种全量更新容易导致重复、闪烁等问题。

正确的实现方式

1. 让ViewModel管理数据变更,发射不可变集合

把ViewModel中的items改为发射不可变List,所有数据修改逻辑放到ViewModel内,每次修改都生成新的集合实例,确保StateFlow能正确发射新值:

// ViewModel示例
class MyViewModel : ViewModel() {
    // 用MutableStateFlow存储不可变List
    private val _items = MutableStateFlow<List<Item>>(emptyList())
    val items: StateFlow<List<Item>> = _items

    // 添加条目的方法,生成新集合触发StateFlow发射
    fun addItem(newItem: Item) {
        _items.update { oldList -> oldList + newItem }
    }

    // 其他修改逻辑(删除、更新)同理,都生成新集合
    fun updateItem(updatedItem: Item) {
        _items.update { oldList ->
            oldList.map { if (it.id == updatedItem.id) updatedItem else it }
        }
    }
}

2. 给RecyclerView Adapter实现高效更新逻辑

不要每次重建Adapter或全量设置数据,而是给Adapter添加submitList方法,用DiffUtil计算新旧数据差异,只更新变化的条目:

// Adapter示例
class MyAdapter : RecyclerView.Adapter<MyAdapter.MyViewHolder>() {
    private var currentItems = emptyList<Item>()

    inner class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        fun bind(item: Item) {
            // 绑定数据逻辑,会在条目需要更新时自动调用
            itemView.tvTitle.text = item.title
            // ...其他视图绑定
        }
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
        val view = LayoutInflater.from(parent.context).inflate(R.layout.item_layout, parent, false)
        return MyViewHolder(view)
    }

    override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
        holder.bind(currentItems[position])
    }

    override fun getItemCount(): Int = currentItems.size

    // 提交新列表,用DiffUtil高效更新
    fun submitList(newItems: List<Item>) {
        val diffCallback = ItemDiffCallback(currentItems, newItems)
        val diffResult = DiffUtil.calculateDiff(diffCallback)
        currentItems = newItems
        diffResult.dispatchUpdatesTo(this)
    }
}

// DiffUtil回调类,判断条目是否相同、内容是否变化
class ItemDiffCallback(private val oldList: List<Item>, private val newList: List<Item>) : DiffUtil.Callback() {
    override fun getOldListSize(): Int = oldList.size
    override fun getNewListSize(): Int = newList.size

    override fun areItemsTheSame(oldPos: Int, newPos: Int): Boolean {
        // 用条目的唯一标识判断是否为同一个条目(比如id)
        return oldList[oldPos].id == newList[newPos].id
    }

    override fun areContentsTheSame(oldPos: Int, newPos: Int): Boolean {
        // 判断条目内容是否一致(可重写Item的equals方法,或手动比较字段)
        return oldList[oldPos] == newList[newPos]
    }
}

3. Fragment中订阅StateFlow,更新Adapter

在Fragment的collect回调中,只需调用Adapter的submitList方法,无需重建Adapter或全量设置:

// Fragment示例
class MyFragment : Fragment() {
    private lateinit var adapter: MyAdapter
    private val viewModel: MyViewModel by viewModels()

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        adapter = MyAdapter()
        recyclerView.adapter = adapter

        // 订阅StateFlow,自动更新列表
        lifecycleScope.launch {
            repeatOnLifecycle(Lifecycle.State.STARTED) {
                viewModel.items.collect { newItems ->
                    adapter.submitList(newItems)
                }
            }
        }

        // 示例:添加条目时调用ViewModel的方法,而非直接修改集合
        btnAddItem.setOnClickListener {
            viewModel.addItem(Item(id = System.currentTimeMillis(), title = "新条目"))
        }
    }
}

为什么这样有效?

  • ViewModel中每次修改数据都生成新的不可变List,StateFlow会自动感知到值变化并发射新数据,确保Fragment能收到更新。
  • DiffUtil会计算新旧列表的差异,只通知Adapter更新变化的条目,避免全量刷新导致的重复、闪烁问题,同时onBindViewHolder会自动为需要更新的条目重新绑定数据。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 07:05:19