配置发生变更时如何避免Fragment重置并保留当前底部导航选中状态?
切换深色/浅色模式属于系统配置变更,默认会触发Activity销毁重建。如果你的Activity在onCreate方法中没有判断是否为重建状态就默认加载首页Fragment,同时没有持久化保存当前选中的tab标识,重建后就会自动回到首页,系统自动恢复的Fragment状态也会被覆盖。
第一步:持久化存储当前选中的tab标识
重写Activity的onSaveInstanceState方法,在销毁前存储当前选中的tab ID,并且每次切换tab时同步更新当前tab的标识变量:// 全局变量,存储当前选中的tab ID private var currentTabId: Int = R.id.tab_home // 初始值为首页tab的menu ID override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) // 存储当前tab ID outState.putInt("KEY_CURRENT_TAB", currentTabId) }第二步:重建时恢复tab状态,避免重复加载首页
在Activity的onCreate中判断savedInstanceState是否为空:空代表首次启动,正常加载首页;非空代表配置变更导致的重建,直接恢复之前选中的tab即可,系统会自动恢复所有已创建的Fragment实例,不需要你手动重新创建:override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val bottomNav = findViewById<BottomNavigationView>(R.id.bottom_nav) if (savedInstanceState == null) { // 首次启动,默认加载首页 currentTabId = R.id.tab_home switchFragment(HomeFragment()) bottomNav.selectedItemId = currentTabId } else { // 重建状态,恢复之前选中的tab currentTabId = savedInstanceState.getInt("KEY_CURRENT_TAB", R.id.tab_home) bottomNav.selectedItemId = currentTabId } // 底部导航选中事件,正常处理tab切换逻辑即可 bottomNav.setOnItemSelectedListener { item -> if (item.itemId != currentTabId) { currentTabId = item.itemId // 切换Fragment建议用show/hide的方式,避免重复创建实例 switchFragment(getFragmentByTabId(item.itemId)) } return@setOnItemSelectedListener true } }注意:切换Fragment时建议使用
supportFragmentManager的show/hide方法操作已创建的实例,不要每次切换都newInstance新的Fragment,既可以提升切换速度,也可以避免重建时出现Fragment重叠的问题。第三步:不要拦截配置变更
不推荐在Manifest中给对应Activity配置android:configChanges="uiMode"来避免重建,该方案会跳过系统自动切换UI资源的逻辑,你需要手动重新加载所有布局、颜色、Drawable等资源,反而容易出现适配问题。按照前两步处理即可保证各Fragment自动加载对应UI模式的资源,不需要额外适配。可选:Fragment状态保存
如果需要保存Fragment内的临时状态(比如列表滚动位置、输入框内容),可以重写对应Fragment的onSaveInstanceState方法存储相关值,在Fragment的onViewCreated中从savedInstanceState取出恢复即可。
内容的提问来源于stack exchange,提问作者moalzoabi

