Получить родительский фрагмент из дочернего, когда используется Navigation Component
Нужно передать данные из одного фрагмента в другой. Сейчас рекомендуемый способ это сделать - использовать общий ViewModel. Чтобы один и тот же экземпляр был доступен в обоих фрагментах нужен какой-то общий owner. В качестве него может выступать их общая Activity. Но при таком подходе(В случае Single Activity), экземпляр ViewModel будет жить на всём протяжении работы приложения. При классическом использовании фрагментов, можно указать ViewModelProvider(this) в родительском фрагменте, и ViewModelProvider(getParentFramgent()) в дочернем. Таким образом scope ViewModel'a ограничится жизнью родительского фрагмента.
Проблема в том, что при использовании Navigation Component getParentFramgent() будет возвращать NavHostFragment,а не родительский фрагмент. Как быть, собственно?
Фрагменты кода:
Навигационный граф:
<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/nav"
app:startDestination="@id/mainMenuFragment">
<fragment
android:id="@+id/mainMenuFragment"
android:name="com.mypackage.mainmenu.MainMenuFragment"
android:label="MainMenu"
tools:layout="@layout/fragment_main_menu">
<action
android:id="@+id/start_game_fragment"
app:destination="@id/gameNav" />
</fragment>
<navigation
android:id="@+id/gameNav"
app:startDestination="@id/gameFragment">
<fragment
android:id="@+id/gameFragment"
android:name="com.mypackage.game.GameFragment"
android:label="@string/app_name"
tools:layout="@layout/fragment_game"/>
</navigation>
</navigation>
Где-то в MainMenuFragment:
override fun startGame(gameSession: GameSession) {
//Так не работает
ViewModelProvider(this)[GameSessionViewModel::class.java].setGameSession(
gameSession
)
findNavController().navigate(R.id.start_game_fragment)
}
GameFragment:
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
gameSessionViewModel =
ViewModelProvider(requireParentFragment())[GameSessionViewModel::class.java].apply {
val session = gameSession.value
)
}
}
Ответы (1 шт):
На самом деле никак.
Вот фрагмент кода из androidx.navigation.fragment.FragmentNavigator:
public NavDestination navigate(@NonNull Destination destination, @Nullable Bundle args,
@Nullable NavOptions navOptions, @Nullable Navigator.Extras navigatorExtras) {
// ...
final FragmentTransaction ft = mFragmentManager.beginTransaction();
// ...
ft.replace(mContainerId, frag);
ft.setPrimaryNavigationFragment(frag);
// ...
}
Под капотом используется FragmentManager, который вызывает replace().
Поэтому дочерний фрагмент не добавляется, а замещается новым, следовательно его и не будет в getParentFramgent().