private val homeViewModel: HomeViewModel by navGraphViewModels(R.id.nested_navigation)
И это работает, но
A. Я никогда не видел, чтобы viewModel инициализировался прямо в глобальной переменной
B. Я не могу передавать переменные внутри фабрики с помощью этого подхода
A.) ViewModel в этом случае инициализируется при первом доступе, поэтому, если вы просто наберете homeViewModel
в onCreate
или onViewCreated
, тогда он будет создан с правильной областью действия.
B.) В этом есть правда, вы определенно можете использовать настраиваемую фабрику с navGraphViewModels
, но что вы действительно хотите (вероятно), так это неявно передать любой из ваших Аргументы фрагмента для ViewModel (обратите внимание, что оба ваших фрагмента должны иметь правильные ключи в своих аргументах, чтобы это работало безопасно) с помощью SavedStateHandle
.
Чтобы получить SavedStateHandle
, вам необходимо использовать ан AbstractSavedStateViewModelFactory
. Чтобы создать его, вы должны создать свою ViewModel внутри onViewCreated
(onCreate
не будет работать с навигационными графами), что проще всего сделать, используя ViewModelLazy
.
Чтобы создать viewModelLazy
, вы можете использовать createViewModelLazy
(то, что написано на банке). Это может определить способ передачи ViewModelStoreOwner
(который является NavBackStackEntry
) и SavedStateRegistryOwner
(который ТАКЖЕ является NavBackStackEntry
).
Таким образом, вы можете поместить это в ваш код, и он должен работать.
inline fun <reified T : ViewModel> SavedStateRegistryOwner.createAbstractSavedStateViewModelFactory(
arguments: Bundle,
crossinline creator: (SavedStateHandle) -> T
): ViewModelProvider.Factory {
return object : AbstractSavedStateViewModelFactory(this, arguments) {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel?> create(
key: String, modelClass: Class<T>, handle: SavedStateHandle
): T = creator(handle) as T
}
}
inline fun <reified T : ViewModel> Fragment.navGraphSavedStateViewModels(
@IdRes navGraphId: Int,
crossinline creator: (SavedStateHandle) -> T
): Lazy<T> {
// Wrapped in lazy to not search the NavController each time we want the backStackEntry
val backStackEntry by lazy { findNavController().getBackStackEntry(navGraphId) }
return createViewModelLazy(T::class, storeProducer = {
backStackEntry.viewModelStore
}, factoryProducer = {
backStackEntry.createAbstractSavedStateViewModelFactory(
arguments = backStackEntry.arguments ?: Bundle(), creator = creator
)
})
}
inline fun <reified T : ViewModel> Fragment.fragmentSavedStateViewModels(
crossinline creator: (SavedStateHandle) -> T
): Lazy<T> {
return createViewModelLazy(T::class, storeProducer = {
viewModelStore
}, factoryProducer = {
createAbstractSavedStateViewModelFactory(arguments ?: Bundle(), creator)
})
}
@Suppress("UNCHECKED_CAST")
inline fun <reified T : ViewModel> Fragment.fragmentViewModels(
crossinline creator: () -> T
): Lazy<T> {
return createViewModelLazy(T::class, storeProducer = {
viewModelStore
}, factoryProducer = {
object : ViewModelProvider.Factory {
override fun <T : ViewModel?> create(
modelClass: Class<T>
): T = creator.invoke() as T
}
})
}
Теперь вы можете сделать
private val homeViewModel: HomeViewModel by navGraphSavedStateViewModels(R.id.nested_navigation) { savedStateHandle ->
HomeViewModel(savedStateHandle)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view)
homeViewModel.someData.observe(viewLifecycleOwner) { someData ->
...
}
}