【问题标题】:How to prevent ViewModel from being cleared when navigating away in BottomNavigationView using Navigation Component使用导航组件在 BottomNavigationView 中导航时如何防止 ViewModel 被清除
【发布时间】:2023-03-11 02:51:02
【问题描述】:

我在保存 Fragment 的 UI 状态时遇到问题,并且在使用 BottomNavigationView 时似乎找不到任何解决方案。这是我当前的设置:

我在 activity_main.xml 中有一个 NavHostFragment 和一个 BottomNavigationView 设置:

    <LinearLayout>
    ...
    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/bottom_nav_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        app:defaultNavHost="true"
        app:navGraph="@navigation/bottom_tab_nav" />

    <com.google.android.material.bottomnavigation.BottomNavigationView
        android:id="@+id/bottom_nav_view"
        android:layout_width="match_parent"
        android:layout_height="72dp"
        app:elevation="6dp"
        app:menu="@menu/bottom_tab_menu"
        app:itemIconTint="@color/tab_btn_color"
        app:itemTextColor="@color/tab_btn_color"
        app:itemTextAppearanceActive="@style/TabText"
        app:itemTextAppearanceInactive="@style/TabText"
        app:itemRippleColor="@color/background"/>
        ...
</LinearLayout>

我在 MainActivity.kt 中设置navController 如下:

class MainActivity : AppCompatActivity() {
    ...
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        ...

        val navHostFragment =
            supportFragmentManager.findFragmentById(R.id.bottom_nav_host_fragment) as NavHostFragment
        val bottomNavController =  navHostFragment.navController
        binding.bottomNavView.setupWithNavController(bottomNavController)

        ...
    }

在我的导航图中,我有两个独立的Fragment 目的地:

  1. HomeFragment,这是我要保存的。
  2. ProfileFragment,当前为空。

目前,HomeFragment 包含我想要迁移到 HomeViewModel 的所有 UI 逻辑,并将我的所有状态保存在其中。这是我的 HomeViewModel.kt 类:

class HomeViewModel: ViewModel() {
    init {
        Log.d(TAG, "HomeViewModel is Initialized.")
    }
    override fun onCleared() {
        super.onCleared()
        Log.d(TAG, "HomeViewModel is removed.")
    }
}

HomeFragment.kt 我初始化我的ViewModel 如下:

class HomeFragment : Fragment() {
    ...
    private lateinit var viewModel: HomeViewModel

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        ...

        viewModel = ViewModelProvider(this)[HomeViewModel::class.java]

        ...
        return binding.root
    }
}

我的问题:

每当我导航到 ProfileFragment 时,我在控制台中什么也得不到,然后当我再次导航回 HomeFragment 时,我会得到以下输出:

D/HomeViewModel: HomeViewModel is removed.
D/HomeViewModel: HomeViewModel is Initialized.

这意味着我的HomeViewModel 正在重新创建。

注意,我没有手动调用任何 navigate 方法在两个片段之间导航,导航是通过设置 menu.xml item 自动完成的id 与 HomeFragmentProfileFragment 目标的 id 相同。

问题:

当我离开 HomeFragment 时,如何保持 HomeViewModel 的状态,以便以后可以将所有 UI 逻辑迁移到它?

此外,关于如何使用ViewModel 管理我的HomeFragment 状态的任何见解都将受到重视。

【问题讨论】:

  • 您使用的是什么版本的导航?只有 Navigation 2.4 及更高版本会保存每个选项卡的状态
  • @ianhanniballake 我刚刚检查过我使用的是 2.3.5。那么我应该改用 2.4.0 吗?在哪里可以找到有关在此新版本中保存每个选项卡状态的任何文档?他们没有办法解决如此普遍的问题,这似乎非常奇怪。
  • @ianhanniballake 谢谢,使用最新版本2.4.0-rc01 解决了这个问题。

标签: android viewmodel bottomnavigationview android-viewmodel


【解决方案1】:

当您离开 Fragment 时,导航组件会在以后销毁它的 View,以便在您导航到它时从头开始创建。在您的HomeFragmentonCreateView 中,每次发生这种情况时,您都会创建一个HomeViewModel 的新实例。相反,您可以通过使用fragment-ktx 提供的by viewModels() 扩展来使用片段范围的ViewModel(请参阅here

import androidx.fragment.app.viewModels

class HomeFragment : Fragment() {
    ...
    private val viewModel by viewModels<HomeViewModel>()

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        ...

        // Do not instantiate a ViewModel here

        ...
        return binding.root
    }
}

【讨论】:

  • 仍然得到相同的输出,不幸的是:(
  • 这似乎是2.3.5 之前的所有导航版本中的一个错误,使用@ianhanniballake 建议的最新版本2.4.0-rc01 解决了这个问题
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-12-24
  • 1970-01-01
  • 2020-04-09
  • 1970-01-01
  • 2021-10-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多