单Activity+BottomNavigation下Fragment中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

