Компонент навигации - как проверить, был ли фрагмент перемещен заново или возвращен обратно

При использовании компонента «Навигация» легко перейти к следующему фрагменту, а также просто вернуться к предыдущему фрагменту.

Navigation.findNavController(v).navigate(R.id.action1);
/*vs*/
Navigation.findNavController(v).popBackStack();

Как я могу в onViewCreated() проверить, как были достигнуты фрагменты назначения startDestination / topLevel? Если он был возвращен обратно, я хочу запустить другой код по сравнению с тем, когда он был заново перемещен.


Обновление благодаря @Thracian за предложения:

Я тестировал:

navController.addOnDestinationChangedListener(new NavController.OnDestinationChangedListener() {
        @Override
        public void onDestinationChanged(@NonNull NavController controller, @NonNull NavDestination destination, @Nullable Bundle arguments) {
            Log.d("MAIN", "New destination ID: " + destination.getDisplayName());
        }
    });

Но он возвращает тот же результат, независимо от того, перехожу ли я к фрагменту напрямую или использую popBack / navigateUp, поэтому он не помогает отличить.

Я тестировал:

navHostFragment = (NavHostFragment) getSupportFragmentManager().findFragmentById(R.id.nav_host_fragment);
    navHostManager = navHostFragment.getChildFragmentManager();
    navHostManager.addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
        @Override
        public void onBackStackChanged() {
            int backStackEntryCount = navHostManager.getBackStackEntryCount();
            int fragmentCount = navHostManager.getFragments().size();

            Log.e("MAIN","backStackEntryCount: "+backStackEntryCount+", fragmentCount: " + fragmentCount);
        }
    });

К сожалению, фрагмент, который я пытаюсь решить, - это startDestination. Следовательно, backStackEntryCount всегда равен 0, когда я его достигаю, как для navigateTo, так и для navigateUp ...

Для других моих пунктов назначения верхнего уровня это также не работает должным образом, поскольку они всегда показывают backStackEntryCount = 1 независимо от того, как я до них добираюсь.


person Tobi    schedule 31.10.2020    source источник
comment
Какой другой код вы пытаетесь запустить? Чем этот код отличается от других случаев, которые вызывают воссоздание представления вашего фрагмента (например, изменения конфигурации)?   -  person ianhanniballake    schedule 31.10.2020
comment
Например, разные переходы MotionLayout   -  person Tobi    schedule 01.11.2020


Ответы (2)


Я наконец решил проблему.

Спасибо @Thracian за то, что указал мне на navHostManager.getBackStackEntryCount() и два слушателя, которые имели решающее значение для отладки.

Я решил это сейчас, переопределив onBackPressed():

@Override
public void onBackPressed(){
    if (navController.getPreviousBackStackEntry() == null ||
            navController.getCurrentBackStackEntry() == null ||
            navController.getCurrentBackStackEntry()
                    .getDestination().getId() == R.id.nav_dashboardFragment)
        Log.d("MAIN", "back pressed on Dashboard");
    else {
        Log.d("MAIN", "Back pressed. Target: " + navController.getPreviousBackStackEntry().getDestination().getDisplayName());

        Bundle args = new Bundle();
        args.putInt(DashboardFragment.KEY_CANCEL_ANIMATION_ID, 1);
        if (navHostManager.getBackStackEntryCount() > 1)
            navController.navigateUp();
        else
            navController.navigate(R.id.nav_dashboardFragment, args,
                    new NavOptions.Builder().setPopUpTo(R.id.nav_dashboardFragment, true).build());
    }
}

Сначала я проверяю, нахожусь ли я уже в пункте назначения. Если нет, я проверяю getBackStackEntryCount() > 1, чтобы понять, будет ли следующее всплывающее окно начальным пунктом назначения или нет. Если следующим будет начальный пункт назначения, я не вызываю navigateUp(), а вместо этого вызываю прямой navigate () с аргументом, который сообщает фрагменту и наиболее важному NavOptions, которые очищают задний стек, чтобы не удваивать startDestination в заднем стеке (не работало без NavOptions).

Затем во фрагменте я добавил:

    if (getArguments() != null && getArguments().get(KEY_CANCEL_ANIMATION_ID) != null) {
        Log.d("DashV", "cancel animation detected");
        motionLayout.setProgress(1);
    }

Отменить анимацию, если аргумент найден.

person Tobi    schedule 06.11.2020

Вы можете использовать addOnBackstackChangeListener для childManager в NavHostFragment, проверить backEntryCount и сравнить его со значением, которое вы сохранили в поле.

Для FragmentContainerView в действии с идентификатором nav_host_fragment

 <androidx.fragment.app.FragmentContainerView
        android:id="@+id/nav_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:defaultNavHost="true"
        app:navGraph="@navigation/nav_graph" />

Вы можете получить fragmentManager с

   val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment)

    val fragmentManager = navHostFragment?.childFragmentManager

И слушайте изменение backStackEntryCount с каждым действием навигации.

    fragmentManager?.addOnBackStackChangedListener {
        val backStackEntryCount = fragmentManager.backStackEntryCount
        val fragmentCount = fragmentManager.fragments.size

        Toast.makeText(
            applicationContext,
            "backStackEntryCount: $backStackEntryCount, fragmentCount: $fragmentCount",
            Toast.LENGTH_SHORT
        ).show()
    }

Вы также можете проверить текущий идентификатор места назначения или сохранить их в поле, если они вам нужны.

   findNavController(R.id.nav_host_fragment)
        .addOnDestinationChangedListener { controller, destination, arguments ->

            Toast.makeText(applicationContext, "Destination: ${destination.id}", Toast.LENGTH_SHORT)
                .show()
        }

И для доступа к этим данным из любого фрагмента используйте ViewModel, который является общим для Activity и фрагментов, и установите значения в Activity и прослушайте фрагменты.

person Thracian    schedule 05.11.2020
comment
Привет, @Thracian, спасибо за ваши предложения. К сожалению, мою проблему они еще не решили. Не могли бы вы взглянуть на мои обновления, где я описываю, почему оба варианта пока не работают? - person Tobi; 06.11.2020
comment
Спасибо за ваши усилия и за то, что указали мне правильное направление. Я назначу награду, как только закончится 24-часовой таймер. - person Tobi; 06.11.2020