26

我正在使用导航组件,我希望在几个片段之间共享一个视图模型,但是当我离开片段时它们应该被清除(因此不将它们限制在活动范围内)我正在尝试将一个活动作为多个片段方法。我已经设法使用多个导航主机来实现这一点,并使用 getParentFragment 将片段范围限定为它,但这只会导致更多的问题,必须将片段包装在其他父片段中,失去后退按钮的无缝工作以及其他黑客让应该工作的东西很简单。有没有人知道如何实现这一目标?我想知道我可以使用 getViewModelStore 是否有任何东西,鉴于下面的图像,我想将视图模型的范围限定为 createCardFragment2 并在它之后的任何内容中使用它(addPredictions、editImageFragment 和其他我没有

顺便说一句,我不能只在 mainFragment 视图模型存储上调用 clear,因为这里还有其他不应该被清除的视图模型,我想我想要一种方法来告诉导航主机我知道的父片段应该是什么不是如果我从 mainFragment 或 cardPreviewFragment 导航,这将成为一件事或一种使视图模型新的方法

导航图

4

4 回答 4

13

这是 Alex H 接受的答案的具体示例。

在你的 build.gradle (app)

dependencies {
    def nav_version = "2.1.0"
    implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
}

视图模型示例

class MyViewModel : ViewModel() { 
    val name: MutableLiveData<String> = MutableLiveData()
}

在您的 FirstFlowFragment.kt 中定义

val myViewModel: MyViewModel by navGraphViewModels(R.id.your_nested_nav_id)
myViewModel.name.value = "Cool Name"

在您的 SecondFlowFragment.kt 中定义

val myViewModel: MyViewModel by navGraphViewModels(R.id.your_nested_nav_id)
val name = myViewModel.name.value.orEmpty()
Log.d("tag", "welcome $name!")

现在 ViewModel 被限定在这个嵌套片段中,当嵌套导航被销毁时共享状态也将被销毁,无需手动重置它们。

于 2019-12-18T07:08:53.497 回答
12

是的,现在可以将视图模型的范围限定为以androidx.navigation:*:2.1.0-alpha02. 请参阅此处的发行说明和此处的 API 示例。您需要提供的只是您的导航图。不过,我觉得使用起来有点烦人,因为通常视图模型是在配置更改的情况)。R.idonCreate

另外,如果您不希望自己mainFragment成为该范围的一部分,我建议您将其取出并使用嵌套导航图

于 2019-06-21T00:17:50.703 回答
4

所以当我发布这个功能时,功能在那里但没有按预期工作,从那时起我现在一直使用这个,这个问题越来越受到关注,所以我想我会发布一个最新的例子,

使用

//Navigation
implementation "androidx.navigation:navigation-fragment:2.2.0-rc04"
// Navigation UI
implementation "androidx.navigation:navigation-ui:2.2.0-rc04"

我得到这样的视图模型商店所有者

private ViewModelStoreOwner getStoreOwner() {

        NavController navController = Navigation
                .findNavController(requireActivity(), R.id.root_navigator_fragment);
        return navController.getViewModelStoreOwner(R.id.root_navigator);
}

我使用一个活动多片段实现,但是使用它我可以有效地将我的视图模型绑定到范围内的片段,并且使用新的实时数据,您甚至可以限制它

第一个 id 来自导航图片段

<?xml version="1.0" encoding="utf-8"?>
  <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <fragment
      android:id="@+id/root_navigator_fragment"
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      android:name="androidx.navigation.fragment.NavHostFragment"
      app:defaultNavHost="true"
      app:navGraph="@navigation/root_navigator"/>

  </FrameLayout>

第二个来自导航图的id

  <?xml version="1.0" encoding="utf-8"?>
  <navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/root_navigator"
    app:startDestination="@id/mainNavFragment">

然后你可以像这样使用它

private void setUpSearchViewModel() {
    searchViewModel = new ViewModelProvider(getStoreOwner()).get(SearchViewModel.class);
}
于 2020-03-05T22:14:17.330 回答
1

因此,根据此处的答案,我创建了一个函数,该函数可以延迟返回ViewModel当前导航图的范围。

     private val scopedViewModel by lazy { getNavScopedViewModel(arg) }


    /**
     * The [navGraphViewModels] function is not entirely lazy, as we need to pass the graph id
     * immediately, but we cannot call [findNavController] to get the graph id from, before the
     * Fragment's [onCreate] has been called. That's why we wrap the call in a function and call it lazily.
     */
    fun getNavScopedViewModel(arg: SomeArg): ScopedViewModel {
        // The id of the parent graph. If you're currently in a destination within this graph
        // it will always return the same id
        val parentGraphScopeId = findNavController().currentDestination?.parent?.id
            ?: throw IllegalStateException("Navigation controller should already be initialized.")
        val viewModel by navGraphViewModels<ScopedViewModel>(parentGraphScopeId) {
            ScopedViewModelFactory(args)
        }

        return viewModel
    }

这不是最漂亮的实现,但它完成了工作

于 2021-12-08T16:02:15.027 回答