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

如何重启ViewPager2中由Activity创建的Fragment?

问题原因

你调用notifyDataSetChanged()、notifyItemChanged()不生效的核心原因有两个:

  • 默认FragmentStateAdapter通过itemId判断页面是否为同一个对象,你没有重写id相关判断逻辑时,系统默认以position作为itemId,只要位置不变就会判定为同一个Fragment,不会触发重建。
  • 你当前的Adapter直接持有外部预创建的Fragment列表,违反了FragmentStateAdapter的设计原则:Fragment实例的创建、生命周期回收应该完全由Adapter和FragmentManager接管,外部自行缓存的Fragment实例很容易和实际挂载的实例不一致,导致刷新逻辑失效。
修复方案

第一步:重写ViewPagerAdapter

不要在构造参数中传入预创建的Fragment列表,改为维护页面标识和Fragment创建逻辑,同时重写id相关方法,新增刷新接口:

class ViewPagerAdapter(
    fragmentManager: FragmentManager,
    lifecycle: Lifecycle
) : FragmentStateAdapter(fragmentManager, lifecycle) {
    // 存储每个页面的唯一ID
    private val pageIds = mutableListOf<Long>()
    // 存储每个页面对应的Fragment创建逻辑,不持有现成Fragment实例
    private val fragmentBuilders = mutableMapOf<Long, () -> Fragment>()

    /**
     * 添加页面
     * @param pageId 页面唯一标识
     * @param builder Fragment创建逻辑
     */
    fun addPage(pageId: Long, builder: () -> Fragment) {
        pageIds.add(pageId)
        fragmentBuilders[pageId] = builder
    }

    override fun getItemCount(): Int = pageIds.size

    override fun createFragment(position: Int): Fragment {
        val currentId = pageIds[position]
        return fragmentBuilders[currentId]?.invoke() ?: throw IllegalArgumentException("对应位置页面不存在")
    }

    override fun getItemId(position: Int): Long = pageIds[position]

    override fun containsItem(itemId: Long): Boolean = pageIds.contains(itemId)

    /**
     * 刷新指定位置的Fragment
     * @param position 要重启的Fragment位置
     */
    fun refreshFragment(position: Int) {
        val oldId = pageIds[position]
        // 生成新ID,让系统判定为全新页面,自动销毁旧实例重建
        val newId = System.currentTimeMillis()
        pageIds[position] = newId
        fragmentBuilders[newId] = fragmentBuilders[oldId]!!
        fragmentBuilders.remove(oldId)
        notifyItemChanged(position)
    }

    /**
     * 刷新所有Fragment
     */
    fun refreshAll() {
        val newPageIds = mutableListOf<Long>()
        val newBuilders = mutableMapOf<Long, () -> Fragment>()
        pageIds.forEachIndexed { index, oldId ->
            val newId = System.currentTimeMillis() + index
            newPageIds.add(newId)
            newBuilders[newId] = fragmentBuilders[oldId]!!
        }
        pageIds.clear()
        pageIds.addAll(newPageIds)
        fragmentBuilders.clear()
        fragmentBuilders.putAll(newBuilders)
        notifyDataSetChanged()
    }
}

第二步:修改MainActivity中的初始化逻辑

移除你自己维护的fragments: MutableList<Fragment>列表,初始化Adapter时通过addPage传入Fragment创建逻辑:

private val tabsArray = arrayOf("Test1", "Test2")

// ...
fragmentPagerAdapter = ViewPagerAdapter(
    supportFragmentManager,
    lifecycle
)
// 注册页面,传入创建逻辑即可,不要提前new Fragment实例
fragmentPagerAdapter.addPage(0L) { Test1Fragment() }
fragmentPagerAdapter.addPage(1L) { Test2Fragment() }

viewPager.adapter = fragmentPagerAdapter
TabLayoutMediator(tabs, viewPager) { tab, position ->
    tab.text = tabsArray[position]
}.attach()
// ...

第三步:需要重启Fragment时直接调用对应方法

  • 重启指定位置的Fragment(比如重启第一个Tab页):
    fragmentPagerAdapter.refreshFragment(0)
    
  • 重启ViewPager2内所有Fragment:
    fragmentPagerAdapter.refreshAll()
    
注意事项
  • 不要在任何业务代码中自行缓存ViewPager2内的Fragment实例,需要获取当前挂载的Fragment时,通过supportFragmentManager.findFragmentByTag("f${fragmentPagerAdapter.getItemId(position)}")获取即可,避免拿到已经被销毁的失效实例。
  • 不要为了方便重写Adapter的getItemId直接返回位置,否则永远无法触发对应位置的Fragment重建。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 20:16:04