主题切换后,即使调用 onDestroyView(),片段 UI 仍可通过其他片段可见

问题描述 投票:0回答:1

这是一个奇怪的现象。我有一个使用选项卡导航的标准应用程序,其中一个

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
}                                                                                     

非常标准的东西。执行以下操作时会出现此问题:

  1. 正常启动应用程序。
  2. 后台应用程序并切换到设置,然后启用或禁用“深色主题”模式。
  3. 切换回应用程序,根据需要切换选项卡
  4. 观察损坏的用户界面

以下是一些屏幕截图,显示执行这些步骤后发生的情况(单击查看大照片):

enter image description here enter image description here enter image description here

正如您所看到的,其他片段的 UI 不知何故仍然在屏幕上,即使包含这些 UI 部分的片段都已分离。此外,我已经确认所有这些旧片段都会调用

onDestroyView()
,这使得它们的 UI 如何或为何仍然可见更加令人费解。还应该说的是,当主题切换时,用户切换回应用程序时,无论如何都会调用
onDestroy
MainActivity
,我认为这是标准的。

为什么会出现此问题?如何解决?谢谢!

android android-fragments android-lifecycle android-theme android-styles
1个回答
0
投票

您的

selectedFragment
是原因,您在OnNavigationItemSelected上分离的那个与前一个不匹配。
因为每次您的 Activity 旋转或主题更改时都会重新创建片段实例:

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
方法将占用更少的内存,但会重新创建视图。
这取决于你。

希望对你有帮助,祝你玩得开心。

© www.soinside.com 2019 - 2024. All rights reserved.