Получить родительский фрагмент от дочернего при использовании компонента навигации - PullRequest
0 голосов
/ 01 марта 2020

Мне нужно перенести данные из одного фрагмента в другой. Теперь рекомендуемый способ сделать это - использовать общий ViewModel. Чтобы получить один и тот же экземпляр в обоих фрагментах, нужен общий владелец. Как это может быть их общее Activity. Но при таком подходе (в случае Single Activity) экземпляр ViewModel будет существовать во всем приложении. В классе c использование фрагментов вы можете указать ViewModelProvider (this) в родительском фрагменте и ViewModelProvider (getParentFramgent ()) в дочернем. Таким образом, область действия ViewModel ограничена сроком жизни родительского фрагмента. Проблема в том, что при использовании компонента навигации getParentFramgent () вернет NavHostFragment, а не родительский фрагмент. Что мне нужно сделать?

Примеры кода :

Где-то в навигационной_графе. xml:

<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) {
            //This approach doesn't work
            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
                    )
                }
    }

РЕДАКТИРОВАТЬ:

Я могу использовать NavHostFragment (возвращается из getParentFragment()) в качестве общего для всех фрагментов , но тогда, как в случае Activity, ViewModel.onCleared() не будет вызвано, когда закончится реальный родительский фрагмент.

1 Ответ

0 голосов
/ 02 марта 2020

Там действительно нет способа сделать это.

Вот фрагмент кода из 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().

...