Как вернуться назад из фрагментов внутри NavHostFragment ViewPager2?

Каков правильный способ возврата из вложенных фрагментов ViewPager2?

Несмотря на использование app:defaultNavHost="true"с FragmentContainerView нажатием кнопки «Назад», когда во вложенном фрагменте страницы вызывается обратное нажатие Activity вместо перехода к предыдущему фрагменту.


person Thracian    schedule 28.06.2020    source источник


Ответы (3)


Согласно создайте документацию NavHostFragment, app:defaultNavHost="true" вызывает setPrimaryNavigationFragment() при первом добавлении фрагмента - это setPrimaryNavigationFragment(), который автоматически направляет события нажатия кнопки назад на этот фрагмент.

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

Поэтому вам нужно подключиться к обратным вызовам, когда фрагмент становится активным фрагментом, и вызывать setPrimaryNavigationFragment(). ViewPager2 1.1.0-alpha01 добавляет именно этот API в FragmentTransactionCallback, в частности, onFragmentMaxLifecyclePreUpdated() , который вызывается всякий раз, когда изменяется состояние жизненного цикла фрагмента: когда оно изменяется на RESUMED, этот фрагмент теперь является активным фрагментом и должен стать основным фрагментом навигации как часть обратного вызова onPost.

private class Adapter(parentFragment: Fragment) : FragmentStateAdapter(parentFragment) {
    init {
        // Add a FragmentTransactionCallback to handle changing
        // the primary navigation fragment
        registerFragmentTransactionCallback(object : FragmentTransactionCallback() {
            override fun onFragmentMaxLifecyclePreUpdated(
                    fragment: Fragment,
                    maxLifecycleState: Lifecycle.State
            ) = if (maxLifecycleState == Lifecycle.State.RESUMED) {
                // This fragment is becoming the active Fragment - set it to
                // the primary navigation fragment in the OnPostEventListener
                OnPostEventListener {
                    fragment.parentFragmentManager.commitNow {
                        setPrimaryNavigationFragment(fragment)
                    }
                }
            } else {
                super.onFragmentMaxLifecyclePreUpdated(fragment, maxLifecycleState)
            }
        })
    }

    // The rest of your FragmentStateAdapter...
}
person ianhanniballake    schedule 29.06.2020
comment
На самом деле это то, что я искал в первую очередь, поскольку я не мог добавить навигацию к FragmentStateAdapter, мне пришлось поместить их в NavHostFragments. - person Thracian; 29.06.2020
comment
Это работает, если NavHostFragment установлен app:defaultNavHost="true"in xml, иначе это не сработало. Кроме того, когда сам ViewPager2 был в NavHostFragment, вы должны установить навигацию по умолчанию на true как для родительского, так и для страницы NavHostFragments. - person Thracian; 29.06.2020

Вам нужно переопределить логику onBackPressed вашей родительской активности, вам нужно использовать https://developer.android.com/reference/androidx/navigation/NavController#popBackStack() для перехода вверх по графу навигации вложенного фрагмента.

person Filip Sollár    schedule 29.06.2020

Вот Java-версия ответа @ianhanniballake (да, я луддит, потому что еще не использую kotlin - мне нужно сначала узнать Java наизнанку, прежде чем я узнаю что-то еще). Я еще не тестировал это, но оно работает...

public class ViewPagerAdapter extends FragmentStateAdapter {
    public ViewPagerAdapter(@NonNull FragmentManager fragmentManager,
                            @NonNull Lifecycle lifecycle) {
        super(fragmentManager, lifecycle);

        registerFragmentTransactionCallback(new FragmentTransactionCallback() {
            @NonNull
            @Override
            public OnPostEventListener onFragmentMaxLifecyclePreUpdated(@NonNull Fragment fragmentArg,
                                                                        @NonNull Lifecycle.State maxLifecycleState) {
                if (maxLifecycleState == Lifecycle.State.RESUMED) {
                    return () -> fragmentArg.getParentFragmentManager().beginTransaction()
                            .setPrimaryNavigationFragment(fragmentArg).commitNow();
                } else {
                    return super.onFragmentMaxLifecyclePreUpdated(fragmentArg, maxLifecycleState);
                }
            }
        });
    }

    // remainder of your FragmentStateAdapter here

}
person tommytucker7182    schedule 31.05.2021