这是一个奇怪的现象。我有一个使用选项卡导航的标准应用程序,其中一个
MainActivity
和四个 Fragment
每当用户单击底部选项卡时就会交换。
以下是片段交换的方式:
val appointmentsFragment = AppointmentsFragment()
val chatFragment = ChatFragment()
val journalFragment = JournalFragment()
val profileFragment = ProfileFragment()
lateinit var selectedFragment: Fragment
if (savedInstanceState == null) {
supportFragmentManager.commit {
setReorderingAllowed(true)
add(R.id.fragmentContainer, appointmentsFragment, appointmentsFragment.javaClass.simpleName)
selectedFragment = appointmentsFragment
}
}
bottomNavigation.setOnNavigationItemSelectedListener {
val selection = when(it.itemId) {
R.id.appointments -> appointmentsFragment
R.id.chat -> chatFragment
R.id.journal -> journalFragment
R.id.profile -> profileFragment
else -> { throw IllegalStateException("There are only 4 tabs.") }
}
supportFragmentManager.commit {
setReorderingAllowed(true)
// Detach previous fragment
detach(selectedFragment)
val tag = selection.javaClass.simpleName
if (supportFragmentManager.findFragmentByTag(tag) == null) {
add(R.id.fragmentContainer, selection, tag)
} else {
attach(supportFragmentManager.findFragmentByTag(tag)!!)
}
selectedFragment = selection
}
true
}
非常标准的东西。执行以下操作时会出现此问题:
以下是一些屏幕截图,显示执行这些步骤后发生的情况(单击查看大照片):
正如您所看到的,其他片段的 UI 不知何故仍然在屏幕上,即使包含这些 UI 部分的片段都已分离。此外,我已经确认所有这些旧片段都会调用
onDestroyView()
,这使得它们的 UI 如何或为何仍然可见更加令人费解。还应该说的是,当主题切换时,用户切换回应用程序时,无论如何都会调用 onDestroy
的 MainActivity
,我认为这是标准的。
为什么会出现此问题?如何解决?谢谢!
您的
selectedFragment
是原因,您在OnNavigationItemSelected上分离的那个与前一个不匹配。
val appointmentsFragment = AppointmentsFragment()
val chatFragment = ChatFragment()
val journalFragment = JournalFragment()
val profileFragment = ProfileFragment()
检查这个
我认为更好的方法是为选项卡项 ID 和片段及其工厂创建一个注册表:
// A simple registry for tab item id to fragment tag, and it's factory.
// Fragment instance will only create when you call that factory.
private val fragmentRegistry: Map<Int, Pair<String, () -> Fragment>> = hashMapOf(
R.id.appointments to (AppointmentsFragment::class.simpleName!! to { AppointmentsFragment() }),
R.id.chat to (ChatFragment::class.simpleName!! to { ChatFragment() }),
R.id.journal to (JournalFragment::class.simpleName!! to { JournalFragment() }),
R.id.profile to (ProfileFragment::class.simpleName!! to { ProfileFragment() })
)
然后只显示选定的片段,并隐藏其他片段:
bottomNavigation.setOnNavigationItemSelectedListener {
// Get selected fragment registry, return or throw exception is ok when null.
val itemId = it.itemId
val (tag, factory) = fragmentRegistry[itemId] ?: return
// Show the one selected
supportFragmentManager.beginTransaction().apply {
var fragment = supportFragmentManager.findFragmentByTag(tag)
if (fragment != null) {
show(fragment).setMaxLifecycle(fragment, Lifecycle.State.RESUMED)
} else {
fragment = factory.invoke()
add(R.id.fragmentContainer, fragment, tag)
}
}.commit()
// Hide the others
supportFragmentManager.beginTransaction().apply {
fragmentRegistry.filterKeys { key -> key != itemId }.values.forEach { value ->
val tagToHide = value.first
val fragmentToHide =
supportFragmentManager.findFragmentByTag(tagToHide) ?: return@forEach
hide(fragmentToHide).setMaxLifecycle(
fragmentToHide,
Lifecycle.State.STARTED
)
}
}.commit()
}
注意,这样我们就不需要处理
savedInstanceState
。这是
show/hide
的方法,它会占用更多内存,但可以避免视图重新创建和闪烁。show/hide
替换为 attach/detach
,并且
attach/detach
方法将占用更少的内存,但会重新创建视图。希望对你有帮助,祝你玩得开心。