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

单Activity+BottomNavigation下Fragment中ViewPager的状态持久化问题

解决方案:保留嵌套Fragment的状态(BottomNavigation + ViewPager)

我之前处理过几乎一模一样的场景,核心是要从主Fragment的管理和嵌套ViewPager的子Fragment状态保存两个层面入手,下面一步步来:

1. 不要用replace(),改用show()/hide()管理BottomNavigation的主Fragment

BottomNavigation默认用replace()切换的话,每次都会销毁非当前Fragment,必然丢失状态。所以在Activity里要维护三个主Fragment的实例,切换时只做显示/隐藏操作:

// 在Activity中初始化三个主Fragment实例
private val homeFragment = HomeFragment()
private val nestedPagerFragment = NestedPagerFragment()
private val profileFragment = ProfileFragment()

// BottomNavigation的选中监听
bottomNavigationView.setOnItemSelectedListener { item ->
    when (item.itemId) {
        R.id.nav_home -> showFragment(homeFragment)
        R.id.nav_pager -> showFragment(nestedPagerFragment)
        R.id.nav_profile -> showFragment(profileFragment)
    }
    true
}

// 核心方法:显示指定Fragment,隐藏其他
private fun showFragment(targetFragment: Fragment) {
    val transaction = supportFragmentManager.beginTransaction()
    // 先检查Fragment是否已经添加过
    if (!targetFragment.isAdded) {
        transaction.add(R.id.fragment_container, targetFragment, targetFragment.javaClass.simpleName)
    }
    // 隐藏所有已添加的Fragment
    listOf(homeFragment, nestedPagerFragment, profileFragment).forEach {
        transaction.hide(it)
    }
    // 显示目标Fragment
    transaction.show(targetFragment)
    transaction.commit()
}

这样第二个主Fragment(NestedPagerFragment)的实例会一直保留,不会被销毁,为它内部的ViewPager状态保存打下基础。

2. 正确配置ViewPager的适配器(嵌套Fragment要用getChildFragmentManager())

在第二个主Fragment里,初始化ViewPager时必须用getChildFragmentManager()而非getFragmentManager()——这是嵌套Fragment的关键,子Fragment需要依附于父Fragment的子FragmentManager:

// 在NestedPagerFragment中
class NestedPagerFragment : Fragment() {
    private lateinit var viewPager: ViewPager2
    private lateinit var adapter: NestedPagerAdapter

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        val view = inflater.inflate(R.layout.fragment_nested_pager, container, false)
        viewPager = view.findViewById(R.id.view_pager)
        // 关键:用getChildFragmentManager()
        adapter = NestedPagerAdapter(childFragmentManager, lifecycle)
        viewPager.adapter = adapter
        // 如果需要彻底保留所有子Fragment,设置offscreenPageLimit为子Fragment总数
        viewPager.offscreenPageLimit = adapter.itemCount
        return view
    }
}

// 适配器根据需求选:固定少量子Fragment用FragmentPagerAdapter,数量多怕内存用FragmentStatePagerAdapter
class NestedPagerAdapter(fm: FragmentManager, lifecycle: Lifecycle) : FragmentStateAdapter(fm, lifecycle) {
    override fun getItemCount() = 3 // 假设你有3个子Fragment

    override fun createFragment(position: Int): Fragment {
        return when(position) {
            0 -> ChildFragment1()
            1 -> ChildFragment2()
            2 -> ChildFragment3()
            else -> ChildFragment1()
        }
    }
}

设置offscreenPageLimit等于子Fragment数量,ViewPager就不会销毁任何子Fragment;如果子Fragment数量多,用FragmentStatePagerAdapter它会销毁不可见页面的视图但保留实例,配合下面的状态保存也能恢复。

3. 子Fragment自己保存视图状态

如果子Fragment有需要留存的UI状态(比如输入框内容、滚动位置),要重写onSaveInstanceState来保存:

class ChildFragment1 : Fragment() {
    private lateinit var editText: EditText
    private val INPUT_TEXT_KEY = "input_text_key"

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        val view = inflater.inflate(R.layout.fragment_child1, container, false)
        editText = view.findViewById(R.id.edit_text)
        // 恢复保存的状态
        savedInstanceState?.let {
            editText.setText(it.getString(INPUT_TEXT_KEY))
        }
        return view
    }

    override fun onSaveInstanceState(outState: Bundle) {
        super.onSaveInstanceState(outState)
        // 保存输入框内容
        outState.putString(INPUT_TEXT_KEY, editText.text.toString())
    }
}

4. 处理系统回收后的状态恢复

如果App进入后台被系统回收,主Fragment和子Fragment可能会被重建,要在Activity的onCreate中恢复之前的实例:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    // 恢复之前的Fragment实例
    if (savedInstanceState != null) {
        homeFragment = supportFragmentManager.findFragmentByTag(HomeFragment::class.java.simpleName) ?: HomeFragment()
        nestedPagerFragment = supportFragmentManager.findFragmentByTag(NestedPagerFragment::class.java.simpleName) ?: NestedPagerFragment()
        profileFragment = supportFragmentManager.findFragmentByTag(ProfileFragment::class.java.simpleName) ?: ProfileFragment()
    }
    // 初始化BottomNavigation...
}

这套操作下来,不管是BottomNavigation切换,还是App后台回收,第二个Fragment及其内部ViewPager的所有子Fragment状态都能完整保留了。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 09:59:24