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

RecyclerView.Adapter变更时刷新Fragment视图的问题(基于Shrine示例)

解决Shrine商品列表刷新的正确姿势

首先咱们来拆解你遇到的问题:

你第一次尝试的代码里,调用theList.pop(1)后notifyDataSetChanged()没生效,大概率是你修改的列表和适配器持有的列表不是同一个引用,或者列表本身是不可变的(比如ProductEntry.initProductEntryList返回的是List而非MutableList)。如果是不可变列表,pop(1)根本不会修改原数据,适配器自然感知不到变化;如果是重新给theList赋值了新列表,但适配器还是拿着旧列表的引用,那通知刷新也没用。

而你编辑1里重新创建适配器的方法虽然能工作,但确实不够高效——每次都要销毁旧适配器、创建新实例、重新绑定所有数据,数据量大的时候会有明显的性能损耗。

正确的高效刷新方式

1. 基础版:复用现有适配器,更新内部列表

首先,确保适配器使用的是可变列表,并且你修改的是适配器持有的同一个列表实例。

第一步,初始化时就把列表转成可变的,并绑定给适配器:

// 把不可变列表转成可变列表,确保后续能修改
val mutableProductList = ProductEntry.initProductEntryList(resources).toMutableList()
adapter = StaggeredProductCardRecyclerViewAdapter(mutableProductList)
// 给RecyclerView设置适配器(这一步很重要,别漏了)
view.recycler_view.adapter = adapter

然后在点击事件里直接修改这个可变列表,再调用适配器的通知方法:

val featured = view.findViewById(R.id.featured) as Button
featured.setOnClickListener {
    if (mutableProductList.isNotEmpty()) {
        mutableProductList.removeAt(0) // 对应你之前的pop(1)操作,移除第一个元素
        // 用细粒度的通知代替notifyDataSetChanged,性能更好
        adapter.notifyItemRemoved(0)
        // 如果后续元素位置有变化,notifyItemRemoved会自动处理移位,不需要额外通知
    }
}

2. 进阶版:切换分类时高效更新数据

如果你是要切换分类(比如加载"featured"分类的商品),不要重新创建适配器,而是给适配器加一个更新数据的方法:

先修改你的适配器类,添加更新数据的函数:

class StaggeredProductCardRecyclerViewAdapter(
    private var productList: MutableList<ProductEntry>
) : RecyclerView.Adapter<StaggeredProductCardRecyclerViewAdapter.ViewHolder>() {

    // 新增:更新数据的方法
    fun updateProductList(newList: List<ProductEntry>) {
        productList.clear()
        productList.addAll(newList)
        // 全量刷新(如果要更高效,用下面的DiffUtil方案)
        notifyDataSetChanged()
    }

    // 其他原有代码...
}

然后在点击事件里调用这个方法:

featured.setOnClickListener {
    val newFeaturedList = ProductEntry.initProductEntryList(resources, "featured").toMutableList()
    adapter.updateProductList(newFeaturedList)
}

3. 最优版:用DiffUtil实现局部刷新

如果列表数据量大,notifyDataSetChanged()会刷新整个列表,还是有点浪费。可以用Android的DiffUtil来计算新旧列表的差异,只刷新真正变化的项:

修改适配器的updateProductList方法:

fun updateProductList(newList: List<ProductEntry>) {
    val diffCallback = object : DiffUtil.Callback() {
        override fun getOldListSize() = productList.size
        override fun getNewListSize() = newList.size

        // 判断两个条目是否是同一个商品(需要ProductEntry有唯一标识,比如id)
        override fun areItemsTheSame(oldPos: Int, newPos: Int): Boolean {
            return productList[oldPos].id == newList[newPos].id
        }

        // 判断两个条目内容是否一致
        override fun areContentsTheSame(oldPos: Int, newPos: Int): Boolean {
            return productList[oldPos] == newList[newPos]
        }
    }

    // 计算差异
    val diffResult = DiffUtil.calculateDiff(diffCallback)
    // 更新内部列表
    productList.clear()
    productList.addAll(newList)
    // 把差异结果分发给适配器,只刷新变化的项
    diffResult.dispatchUpdatesTo(this)
}

这样切换分类时,RecyclerView只会刷新新增、删除或内容变化的项,性能会好很多。

关键总结

  • 永远不要每次数据变化就重新创建适配器,复用现有实例才是高效做法
  • 确保你修改的是适配器持有的列表实例,或者通过适配器的方法更新内部数据
  • 优先用细粒度的通知(如notifyItemRemoved、notifyItemAdded)或DiffUtil代替全量的notifyDataSetChanged

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.09 18:57:46