Как программно изменить начальный пункт назначения навигационного графика [Jetpack]

По сути, у меня есть следующий график навигации:

enter image description here

Я хочу изменить свою начальную точку на навигационном графике на fragment 2 сразу после его достижения (во избежание возврата к fragment 1 при нажатии кнопки "назад" - как на заставке).

Это мой код:

navGraph = navController.getGraph();
navGraph.setStartDestination(R.id.fragment2);
navController.setGraph(navGraph);

Но, очевидно, он не работает и возвращается к fragment 1 после нажатия кнопки назад.

Я делаю это неправильно? Есть ли другое решение?

Ответы

Ответ 1

UPDATE:

Если у вас есть навигационный график, например:

<fragment
    android:id="@+id/firstFragment"
    android:name="com.appname.package.FirstFragment" >
    <action
        android:id="@+id/action_firstFragment_to_secondFragment"
        app:destination="@id/secondFragment" /> 
</fragment>

<fragment
    android:id="@+id/secondFragment"
    android:name="com.appname.package.SecondFragment"/>

И вы хотите перейти ко второму фрагменту и сделать его корнем своего графика, укажите следующий NavOptions:

NavOptions navOptions = new NavOptions.Builder()
        .setPopUpTo(R.id.firstFragment, true)
        .build();

И используйте их для навигации:

Navigation.findNavController(view).navigate(R.id.action_firstFragment_to_secondFragment, bundle, navOptions);

setPopUpTo(int destinationId, boolean inclusive) - всплыть до заданного пункта назначения перед навигацией. При этом все несоответствующие назначения извлекаются из заднего стека, пока этот адресат не будет найден.

destinationId - пункт назначения для всплывающего окна, очищающий все промежуточные пункты назначения.

inclusive - true, чтобы извлекать заданное место назначения из заднего стека.


АЛЬТЕРНАТИВА:

<fragment
    android:id="@+id/firstFragment"
    android:name="com.appname.package.FirstFragment" >
<action
    android:id="@+id/action_firstFragment_to_secondFragment"
    app:destination="@id/secondFragment"
    app:popUpTo="@+id/firstFragment"
    app:popUpToInclusive="true" /> 
</fragment>

<fragment
    android:id="@+id/secondFragment"
    android:name="com.appname.package.SecondFragment"/>

А потом в вашем коде:

findNavController(fragment).navigate(
    FirstFragmentDirections.actionFirstFragmentToSecondFragment())

Старый ответ

Устаревшее: атрибут clearTask для действий и связанного API в NavOptions устарел.

Источник: https://developer.android.com/jetpack/docs/release-notes


Если вы хотите изменить свой корневой фрагмент на fragment 2 (например, после нажатия кнопки "Назад" на fragment 2 вы выйдете из приложения), вам следует добавить следующий атрибут в action или destination:

app:clearTask="true"

Практически это выглядит следующим образом:

<fragment
    android:id="@+id/firstFragment"
    android:name="com.appname.package.FirstFragment"
    android:label="fragment_first" >
    <action
        android:id="@+id/action_firstFragment_to_secondFragment"
        app:destination="@id/secondFragment"
        app:clearTask="true" /> 
</fragment>

<fragment
    android:id="@+id/secondFragment"
    android:name="com.appname.package.SecondFragment"
    android:label="fragment_second"/>

Я добавил app:clearTask="true" к действию.


Теперь, когда вы выполняете переход от fragment 1 к fragment 2, используйте следующий код:

Navigation.findNavController(view)
        .navigate(R.id.action_firstFragment_to_secondFragment);

Ответ 2

Я нашел решение для этого, но это уродливо. Я предполагаю, что этого следовало ожидать с альфа-библиотекой, но я надеюсь, что Google будет упрощать/исправлять это, поскольку это довольно популярный шаблон навигации.

Решение Алексея не сработало для меня. Моя проблема заключалась в том, что у меня есть стрелки, показанные на моей панели действий, используя:

NavigationUI.setupActionBarWithNavController(this, navController)

Если бы я сделал это, как предлагает Алексей, у моего нового фрагмента начала была стрелка, указывающая на мой первоначальный фрагмент начала. Если бы я нажал эту стрелку вверх, мое приложение будет сортировать перезагрузку, переходя к себе (новый начальный фрагмент)

Вот код, необходимый для получения того, что я хотел:

  • Фрагмент # 1 - это то, где мое приложение изначально начинается
  • Я могу выполнить проверку Auth в фрагменте # 1, а затем программно изменить начало на фрагмент # 2.
  • Как только во Фрагменте № 2 нет стрелки вверх, и нажатие кнопки "Назад" не приведет вас к фрагменту №1.

Вот код, который выполняет это. В моей деятельности onCreate:

// Setup the toolbar
val toolbar = findViewById<Toolbar>(R.id.toolbar)
setSupportActionBar(toolbar)
supportActionBar?.setDisplayHomeAsUpEnabled(false)

// Configure the navigation
val navHost = nav_host_fragment as NavHostFragment
graph = navHost.navController
        .navInflater.inflate(R.navigation.nav_graph)
graph.startDestination = R.id.welcomeFragment

// This seems to be a magical command. Not sure why it needed :(
navHost.navController.graph = graph

NavigationUI.setupActionBarWithNavController(this, navHost.navController)

а также:

fun makeHomeStart(){
    graph.startDestination = R.id.homeFragment
}

Затем во Фрагменте №1 по Активному Искусству, по предложению Алексея:

override fun onActivityCreated(savedInstanceState: Bundle?) {
    ...

    // Check for user authentication
    if(sharedViewModel.isUserAuthenticated()) {

        (activity as MainActivity).makeHomeStart() //<---- THIS is the key
        val navOptions = NavOptions.Builder()
                .setPopUpTo(R.id.welcomeFragment, true)
                .build()
        navController.navigate(R.id.action_welcomeFragment_to_homeFragment,null,navOptions)

    } else {
        navController.navigate(R.id.action_welcomeFragment_to_loginFragment)
    }
}

Ключевым кодом является: (activity as MainActivity).makeHomeStart() которая просто запускает метод в действии, который изменяет начало начала диаграмм. Я мог бы очистить это и превратить его в интерфейс, но я буду ждать Google и надеюсь, что они улучшат весь этот процесс. Метод setPopUpTo кажется мне плохо названным, и это не интуитивно понятно, что вы называете фрагмент, который вырезается из графика. Мне также странно, что они вносят эти изменения в navOptions. Я думаю, что navOptions будет относиться только к навигационному действию, к которому они подключены.

И я даже не знаю, что такое navHost.navController.graph = graph, но без него вернутся стрелки вверх. :(

Я использую Navigation 1.0.0-alpha06.

Ответ 3

Вам не нужно вставлять всплеск фрагмент. Он может оставаться там до конца жизни вашего приложения. Что вы должны сделать, это с помощью экрана-заставки определить, какой следующий экран будет отображаться.

Login State Machine flow

На картинке выше вы можете спросить в состоянии экрана-заставки, есть ли сохраненный LoginToken. Если он пуст, перейдите на экран входа в систему.

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

Когда кнопка "Назад" нажата на главном экране, вы отправите сообщение "Результат" на экран-заставку, в котором будет указано, что нужно завершить приложение.