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

如何避免ViewPager2内RecyclerView更新数据后自动滚动到顶部

解决方案:更新RecyclerView单行数据且避免滚动到顶部

核心问题分析

当前代码的主要问题在于:

  • 数据通过Fragment构造函数传递,无法后续更新
  • 未保留RecyclerView适配器引用,无法精准刷新单个item
  • 重建适配器会触发视图重置,导致滚动到顶部

下面是分步修改方案:


1. 改造ProductsFragment:支持数据更新与精准刷新

将Fragment的构造传参改为newInstance静态方法,保留RecyclerView适配器引用,并提供单个item更新接口:

class ProductsFragment : BaseFragment() {
    private lateinit var products: MutableList<Product>
    private lateinit var listAdapter: ListItemAdapter

    companion object {
        private const val ARG_PRODUCTS = "arg_products"
        fun newInstance(products: List<Product>): ProductsFragment {
            return ProductsFragment().apply {
                arguments = Bundle().apply {
                    putParcelableArrayList(ARG_PRODUCTS, ArrayList(products))
                }
            }
        }
    }

    override fun getLayoutRes(): Int = R.layout.fragment_list_item

    override fun setupView(view: View) {
        // 从arguments中获取初始数据并转为可变列表
        products = arguments?.getParcelableArrayList<Product>(ARG_PRODUCTS)?.toMutableList() ?: mutableListOf()
        listAdapter = ListItemAdapter(products)
        binding.rvList.adapter = listAdapter
    }

    // 对外暴露更新单个产品选中状态的方法
    fun updateProductSelection(productId: String, isSelected: Boolean) {
        val targetIndex = products.indexOfFirst { it.id == productId }
        if (targetIndex != -1) {
            products[targetIndex].isSelected = isSelected
            // 仅刷新目标item,避免全局重置导致滚动
            listAdapter.notifyItemChanged(targetIndex)
        }
    }
}

2. 修改FragmentStateAdapter:支持数据更新与Fragment获取

重命名适配器避免与RecyclerView适配器混淆,保留可变数据源,并提供获取指定位置Fragment的方法:

class ProductPagerAdapter(
    fragment: Fragment,
    private var productWithCategories: MutableList<ProductWithCategory>
) : FragmentStateAdapter(fragment) {

    override fun getItemCount() = productWithCategories.size

    override fun createFragment(position: Int): Fragment {
        return ProductsFragment.newInstance(productWithCategories[position].products)
    }

    // 更新全局数据源(可选,用于批量更新分类数据)
    fun updateFullData(newData: List<ProductWithCategory>) {
        productWithCategories.clear()
        productWithCategories.addAll(newData)
        notifyDataSetChanged()
    }

    // 获取指定tab位置的ProductsFragment,用于触发单个item更新
    fun getTargetFragment(position: Int): ProductsFragment? {
        return fragmentManager.findFragmentByTag("f$position") as? ProductsFragment
    }
}

3. 初始化PagerAdapter并保留引用

在宿主Activity/Fragment中全局保留PagerAdapter引用,方便后续操作:

// 宿主类中声明全局变量
private lateinit var productPagerAdapter: ProductPagerAdapter

private fun setupTabLayout(productWithCategory: List<ProductWithCategory>) {
    tabMenuBinding.apply {
        productPagerAdapter = ProductPagerAdapter(
            fragment = this@YourHostClass, // 替换为当前Activity/Fragment的this
            productWithCategories = productWithCategory.toMutableList()
        )
        viewPager.adapter = productPagerAdapter

        TabLayoutMediator(tabLayout, viewPager) { tab, position ->
            tab.text = productWithCategory[position].categoryName
        }.attach()
    }
}

4. 触发单个item更新的调用示例

当需要修改某个产品的选中状态时,直接调用Fragment的更新方法即可:

// 已知产品所在的tab位置和产品ID时
fun updateSingleProduct(tabPosition: Int, productId: String, isSelected: Boolean) {
    val targetFragment = productPagerAdapter.getTargetFragment(tabPosition)
    targetFragment?.updateProductSelection(productId, isSelected)
}

// 未知tab位置时,遍历所有Fragment查找目标产品
fun updateProductByID(productId: String, isSelected: Boolean) {
    for (position in 0 until productPagerAdapter.itemCount) {
        productPagerAdapter.getTargetFragment(position)?.updateProductSelection(productId, isSelected)
    }
}

进阶优化:用DiffUtil提升刷新效率

如果需要批量更新数据,可在RecyclerView适配器中使用DiffUtil避免不必要的视图刷新:

class ListItemAdapter(private val products: MutableList<Product>) : RecyclerView.Adapter<ListItemAdapter.ViewHolder>() {

    // ... 省略ViewHolder和绑定逻辑

    fun updateData(newProducts: List<Product>) {
        val diffResult = DiffUtil.calculateDiff(object : DiffUtil.Callback() {
            override fun getOldListSize() = products.size
            override fun getNewListSize() = newProducts.size
            override fun areItemsTheSame(oldPos: Int, newPos: Int): Boolean {
                return products[oldPos].id == newProducts[newPos].id
            }
            override fun areContentsTheSame(oldPos: Int, newPos: Int): Boolean {
                return products[oldPos] == newProducts[newPos]
            }
        })
        products.clear()
        products.addAll(newProducts)
        diffResult.dispatchUpdatesTo(this)
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 08:20:25