切换主题后已调用onDestroyView()的Fragment UI仍重叠显示
问题成因与解决方案
问题成因
切换系统深色主题时,Android会销毁并重建MainActivity(这是系统正常行为),你的代码存在两个核心问题:
- Fragment实例不匹配:Activity重建时,
savedInstanceState != null,代码里的全局appointmentsFragment等变量会重新创建新实例,但FragmentManager会自动恢复之前状态中存在的所有Fragment实例(包括之前被detach的),导致全局变量指向的实例和FragmentManager实际管理的实例并非同一个。 - 无效的detach操作:后续切换标签时,你detach的是全局变量的新实例(该实例从未被添加到FragmentManager),而FragmentManager恢复的旧实例依然存在于容器中,多个Fragment的UI层叠显示,最终引发错乱。
此外,重建后FragmentManager已存在对应tag的实例,执行attach操作时旧的detached实例未被清理,进一步加重了UI重叠问题。
解决方案
方案1:修复实例匹配(基于detach/attach逻辑)
初始化时优先从FragmentManager恢复已存在的Fragment实例,确保全局变量与FragmentManager管理的实例一致:
class MainActivity : AppCompatActivity() { private val appointmentsFragment by lazy { supportFragmentManager.findFragmentByTag(AppointmentsFragment::class.java.simpleName) ?: AppointmentsFragment() } private val chatFragment by lazy { supportFragmentManager.findFragmentByTag(ChatFragment::class.java.simpleName) ?: ChatFragment() } private val journalFragment by lazy { supportFragmentManager.findFragmentByTag(JournalFragment::class.java.simpleName) ?: JournalFragment() } private val profileFragment by lazy { supportFragmentManager.findFragmentByTag(ProfileFragment::class.java.simpleName) ?: ProfileFragment() } private lateinit var selectedFragment: Fragment override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) if (savedInstanceState == null) { supportFragmentManager.commit { setReorderingAllowed(true) add(R.id.fragmentContainer, appointmentsFragment, AppointmentsFragment::class.java.simpleName) selectedFragment = appointmentsFragment } } else { // 重建时从容器中获取当前显示的Fragment selectedFragment = supportFragmentManager.findFragmentById(R.id.fragmentContainer) ?: appointmentsFragment } bottomNavigation.setOnNavigationItemSelectedListener { item -> val selection = when(item.itemId) { R.id.appointments -> appointmentsFragment R.id.chat -> chatFragment R.id.journal -> journalFragment R.id.profile -> profileFragment else -> error("Only 4 tabs are available") } if (selectedFragment == selection) return@setOnNavigationItemSelectedListener true supportFragmentManager.commit { setReorderingAllowed(true) detach(selectedFragment) attach(selection) selectedFragment = selection } true } } }
方案2:改用hide/show实现标签切换(更稳定)
对于底部标签这种频繁切换的场景,hide/show仅隐藏UI、不会销毁Fragment视图,性能更优且能避免重建时的实例匹配问题:
class MainActivity : AppCompatActivity() { private val fragments = listOf( AppointmentsFragment::class.java.simpleName to AppointmentsFragment(), ChatFragment::class.java.simpleName to ChatFragment(), JournalFragment::class.java.simpleName to JournalFragment(), ProfileFragment::class.java.simpleName to ProfileFragment() ).associate { (tag, frag) -> tag to (supportFragmentManager.findFragmentByTag(tag) ?: frag) } private lateinit var selectedFragment: Fragment override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) if (savedInstanceState == null) { supportFragmentManager.commit { setReorderingAllowed(true) // 一次性添加所有Fragment,默认隐藏非初始页 fragments.values.forEach { frag -> add(R.id.fragmentContainer, frag, frag::class.java.simpleName) if (frag !is AppointmentsFragment) hide(frag) } selectedFragment = fragments[AppointmentsFragment::class.java.simpleName]!! } } else { // 重建时找到当前可见的Fragment selectedFragment = fragments.values.firstOrNull { it.isVisible } ?: fragments[AppointmentsFragment::class.java.simpleName]!! // 确保其他Fragment都处于隐藏状态 fragments.values.filter { it != selectedFragment }.forEach { frag -> if (frag.isVisible) { supportFragmentManager.commit { hide(frag) } } } } bottomNavigation.setOnNavigationItemSelectedListener { item -> val selection = when(item.itemId) { R.id.appointments -> fragments[AppointmentsFragment::class.java.simpleName]!! R.id.chat -> fragments[ChatFragment::class.java.simpleName]!! R.id.journal -> fragments[JournalFragment::class.java.simpleName]!! R.id.profile -> fragments[ProfileFragment::class.java.simpleName]!! else -> error("Only 4 tabs are available") } if (selectedFragment == selection) return@setOnNavigationItemSelectedListener true supportFragmentManager.commit { setReorderingAllowed(true) hide(selectedFragment) show(selection) } selectedFragment = selection true } } }
内容的提问来源于stack exchange,提问作者Orbit
相关产品推荐
相关产品推荐

