Доступ к графической области ViewModel дочернего NavHostFragment с помощью navGraphViewModels

Я использую навигационный компонент Android Jetpack (2.2.0-alpha01).

Я хочу использовать дочерний NavHostFragment, вложенный в мой основной NavHostFragment, оснащенный собственным дочерним графом навигации. Пожалуйста, просмотрите следующее изображение для контекста:

Дочерний навигационный хост определяется следующим образом внутри фрагмента, который находится в передней части стека MainNavHost:

<fragment
        android:id="@+id/childNavHostFragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:defaultNavHost="false"
        app:navGraph="@navigation/child_graph" />

Внутри фрагмента, который находится в передней части фрагмента хоста CHILD Nav, я пытаюсь получить область видимости, связанную с R.navigation.child_graph, используя следующий код:

private val childGraphScopedViewModel: ChildGraphScopedViewModel by navGraphViewModels(R.navigation.child_graph) {
    viewModelFactory
}

При доступе к childGraphScopedViewModel я получаю сбой с сообщением об ошибке:

java.lang.IllegalArgumentException: No NavGraph with ID 2131689472 is on the NavController's back stack.

Я считаю ленивый вызов init by navGraphViewModel() ищет navgraph внутри mainGraph.

Как я могу получить доступ к дочерней модели NavHostFragment в области ViewModel? Спасибо за уделенное время.

0 ответов

Это работает для меня без определения aby custom ViewModelProvider (2.2.0):

private val viewModel: ChildGraphScopedViewModel by navGraphViewModels(R.id. child_graph)

Легкая ошибка - использовать R.navigation.child_graph (плохо) вместо R.id.child_graph (хороший)

Вы можете сделать это, предоставив viewModelStore дочернего элемента NavController

override fun onViewCreated(
     view: View, 
     savedInstanceState: Bundle?
) {
    super.onViewCreated(view, savedInstanceState)

    val childHostFragment = childFragmentManager
          .findFragmentById(R.id.childNavHostFragment) as NavHostFragment

    val childNavController = childHostFragment.navController

    val childViewModel: ChildGraphScopedViewModel = ViewModelProvider(
         childNavController.getViewModelStoreOwner(R.navigation.child_graph)
    ).get(ChildGraphScopedViewModel::class.java)
}

Я написал расширение Kotlin, чтобы было проще

inline fun <reified T: ViewModel> NavController.viewModel(@NavigationRes navGraphId: Int): T {
    val storeOwner = getViewModelStoreOwner(navGraphId)
    return ViewModelProvider(storeOwner)[T::class.java]
}

Применение

val viewModel = findNavController().viewModel(R.navigation.nav)

По какой-то причине создание навигационного графа и использование includeвложить его в основной навигационный граф у меня не получалось. Наверное, это ошибка.

Вы можете выбрать все фрагменты, которые необходимо сгруппировать внутри навигационного графа, и right-click->move to nested graph->new graph

теперь это переместит выбранные фрагменты во вложенный граф внутри основного навигационного графа следующим образом:

<navigation app:startDestination="@id/homeFragment" ...>
    <fragment android:id="@+id/homeFragment" .../>
    <fragment android:id="@+id/productListFragment" .../>
    <fragment android:id="@+id/productFragment" .../>
    <fragment android:id="@+id/bargainFragment" .../>

    <navigation 
        android:id="@+id/checkout_graph" 
        app:startDestination="@id/cartFragment">

        <fragment android:id="@+id/orderSummaryFragment".../>
        <fragment android:id="@+id/addressFragment" .../>
        <fragment android:id="@+id/paymentFragment" .../>
        <fragment android:id="@+id/cartFragment" .../>

    </navigation>

</navigation>

Теперь внутри фрагментов при инициализации ViewModel сделайте это

val viewModel: CheckoutViewModel by navGraphViewModels(R.id.checkout_graph)

Если вам нужно передать фабрику модели просмотра (может быть, для внедрения модели просмотра), вы можете сделать это следующим образом:

val viewModel: CheckoutViewModel by navGraphViewModels(R.id.checkout_graph) { viewModelFactory }