实现类似Instagram的BottomNavigationView 保留子Fragment展示状态
核心实现逻辑和Instagram保持一致:每个底部导航Tab对应的一级Fragment持有独立的子回退栈,切换Tab时仅切换一级Fragment的显隐状态,不销毁实例,即可保留Tab内部的子Fragment浏览状态。
具体实现步骤
1. 一级Fragment初始化(避免销毁重建)
不要使用replace加载BNV对应的一级Fragment(A/B/C/D),replace会销毁旧Fragment实例,内部状态自然丢失。初始化时一次性将所有一级Fragment添加到容器,默认仅显示首个Tab对应的Fragment:
// 提前定义每个一级Fragment的唯一Tag private const val TAG_A = "fragment_a" private const val TAG_B = "fragment_b" private const val TAG_C = "fragment_c" private const val TAG_D = "fragment_d" // Activity onCreate 中初始化 if (supportFragmentManager.findFragmentByTag(TAG_A) == null) { // 仅首次创建时添加Fragment,页面重建时复用已有的实例 supportFragmentManager.beginTransaction().apply { add(R.id.activity_container, FragmentA(), TAG_A) add(R.id.activity_container, FragmentB(), TAG_B) add(R.id.activity_container, FragmentC(), TAG_C) add(R.id.activity_container, FragmentD(), TAG_D) // 隐藏非首个Tab的Fragment hide(supportFragmentManager.findFragmentByTag(TAG_B)!!) hide(supportFragmentManager.findFragmentByTag(TAG_C)!!) hide(supportFragmentManager.findFragmentByTag(TAG_D)!!) show(supportFragmentManager.findFragmentByTag(TAG_A)!!) commitNow() } }
2. 调整BNV切换逻辑
切换Tab时仅做一级Fragment的显隐切换,不做remove、replace操作:
bottomNavigationView.setOnItemSelectedListener { menuItem -> val targetTag = when (menuItem.itemId) { R.id.menu_a -> TAG_A R.id.menu_b -> TAG_B R.id.menu_c -> TAG_C R.id.menu_d -> TAG_D else -> return@setOnItemSelectedListener false } val currentVisibleFragment = supportFragmentManager.fragments.find { it.isVisible } val targetFragment = supportFragmentManager.findFragmentByTag(targetTag) if (currentVisibleFragment != null && targetFragment != null && currentVisibleFragment != targetFragment) { supportFragmentManager.beginTransaction() .hide(currentVisibleFragment) .show(targetFragment) .setReorderingAllowed(true) .commitNow() } true }
3. Fragment B内部子页面跳转逻辑
Fragment B内跳转到Fragment E时,必须使用B自己的childFragmentManager处理子Fragment事务,子栈会和B的生命周期绑定,只要B不被销毁,内部栈状态就会完整保留:
// Fragment B的按钮点击事件中执行 val fragmentE = FragmentE() childFragmentManager.beginTransaction() .replace(R.id.b_inner_container, fragmentE) // 如果需要支持返回键从E退回B的原生页面,添加以下行 .addToBackStack(null) .commit()
4. 返回键逻辑适配(可选)
如果需要实现点击返回键先回退当前Tab内部的子页面,再退出应用,重写Activity的onBackPressed方法:
override fun onBackPressed() { val currentTabFragment = supportFragmentManager.fragments.find { it.isVisible } // 优先回退当前Tab的子栈 if (currentTabFragment?.childFragmentManager?.backStackEntryCount ?: 0 > 0) { currentTabFragment?.childFragmentManager?.popBackStack() } else { super.onBackPressed() } }
注意:不要将一级Fragment的切换事务添加到Activity的回退栈,避免BNV切换逻辑和返回键逻辑冲突。
内容的提问来源于stack exchange,提问作者Tarek Ben Driss
相关产品推荐
相关产品推荐

