Можно ли установить startDestination условно с помощью компонента архитектуры навигации Android (Android Jetpack)?

Я использую Навигация из Android Jetpack для перехода между экранами. Теперь я хочу динамически установить startDestination.

У меня есть действие с именем MainActivity И два фрагмента, FragmentA и FragmentB.

var isAllSetUp : Boolean = // It is dynamic and I’m getting this from Preferences.

    If(isAllSetUp)
    {
     // show FragmentA
    }
    else
    {
     //show FragmentB
    }

Я хочу установить вышеуказанный поток с помощью компонента архитектуры навигации. В настоящее время я использовал startDestionation, как показано ниже, но он не соответствует моим требованиям.

<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/lrf_navigation"
   app:startDestination = "@id/fragmentA">

   <fragment
       android:id = "@+id/fragmentA"
       android:name = "com.mindinventory.FragmentA"
       android:label = "fragment_a"
       tools:layout = "@layout/fragment_a" />
</navigation>

Можно ли установить startDestination условно с помощью компонента архитектуры навигации Android?

# навигационный-архитектурный-компонент # навигационный-граф

Akash Patel 20.08.2018 13:00

проверьте это stackoverflow.com/questions/51173002/…

AskNilesh 20.08.2018 13:01

Привет @NileshRathod! Это сработает, если я сначала открою FragmentA, а затем FragmentB. но я хочу программно установить StartDestination. У вас есть какое-нибудь решение?

Akash Patel 20.08.2018 13:16

Попробуй этот NavigationUI.onNavDestinationSelected(menuItem, navController.getNavController());

AskNilesh 20.08.2018 13:26

Я хочу сделать вышеперечисленное без меню или любого другого элемента управления навигацией. возможно, нет возможности установить startDestination условно. так что на данный момент я использовал [stackoverflow.com/questions/51173002/…, как вы предложили.

Akash Patel 21.08.2018 07:33

В этот ответ добавлено динамическое расширение: Динамическое изменение пункта назначения

Morteza Rastgoo 18.07.2020 14:08
71
6
29 689
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

Ответ принят как подходящий

Наконец-то я получил ответ на свой запрос ...

Поместите ниже код в метод onCreate()Activity.

Код Котлина

val navHostFragment = (supportFragmentManager.findFragmentById(R.id.home_nav_fragment) as NavHostFragment)
val inflater = navHostFragment.navController.navInflater
val graph = inflater.inflate(R.navigation.nav_main)
graph.setDefaultArguments(intent.extras)
graph.startDestination = R.id.fragment1
//or
//graph.startDestination = R.id.fragment2

navHostFragment.navController.graph = graph

Код Java

NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager().findFragmentById(R.id.home_nav_fragment);  // Hostfragment
NavInflater inflater = navHostFragment.getNavController().getNavInflater();
NavGraph graph = inflater.inflate(R.navigation.nav_main);
graph.setDefaultArguments(getIntent().getExtras());
graph.setStartDestination(R.id.fragment1);

navHostFragment.getNavController().setGraph(graph);
navHostFragment.getNavController().getGraph().setDefaultArguments(getIntent().getExtras());


NavigationView navigationView = findViewById(R.id.navigationView);
NavigationUI.setupWithNavController(navigationView, navHostFragment.getNavController());

Дополнительная информация

Как предлагает @artnest, удалите атрибут app:navGraph из макета. После удаления это будет выглядеть примерно так

<?xml version = "1.0" encoding = "utf-8"?>
<FrameLayout 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:layout_width = "match_parent"
    android:layout_height = "match_parent">

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

</FrameLayout>

В случае использования тега fragment вместо FragmentContainerView вышеуказанные изменения остаются прежними.

Во-первых, вам нужно удалить атрибут app: navGraph NavHostFragment из XML-макета Activity, поскольку NavHostFragment создает экземпляр и устанавливает граф навигации после вызова Activity onCreate () хоста. Также, если вы используете метод NavigationUI.setupWithNavController, например, для панели инструментов или BottomNavigationView, вам необходимо поместить этот фрагмент кода над вызовом метода NavigationUI.setupWithNavController.

artnest 29.08.2018 14:12

Я бы также добавил, что для сохранения поведения кнопки возврата в коде Kotlin вам все равно нужен способ установить navHostFragment в качестве хоста навигации по умолчанию. Это можно сделать с помощью этой транзакции: supportFragmentManager.beginTransaction().setPrimaryNavigati‌​onFragment(navHostFr‌​agment).commit()

Chris 30.10.2018 16:47

метода graph.setDefaultArguments () больше не существует, вам также не нужны последние 2 строки (NavigationUI). См. мой ответ для получения обновленной версии.

Danish Khan 01.01.2019 04:05

Можно ли добавить анимацию перехода при настройке графика?

Malik Motani 25.03.2020 08:25

Будет ли работать глубинная ссылка, поскольку она добавляет startDestination, когда вы переходите к глубокому пункту назначения? Что он добавит с этой настройкой?

uberchilly 24.08.2020 21:31

На самом деле это не работает для меня. Ответ @ DanishKhan имеет значение.

Ben Butterworth 08.12.2020 02:19

Некоторые API-интерфейсы были изменены, недоступны или не требуются с Ответ Акаша. Теперь это немного проще.

MainActivity.java:

@Override
protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);

  NavHostFragment navHost = (NavHostFragment) getSupportFragmentManager().findFragmentById(R.id.fragment_main_nav_host);
  NavController navController = navHost.getNavController();

  NavInflater navInflater = navController.getNavInflater();
  NavGraph graph = navInflater.inflate(R.navigation.navigation_main);

  if (false) {
    graph.setStartDestination(R.id.oneFragment);
  } else {
    graph.setStartDestination(R.id.twoFragment);
  }

  navController.setGraph(graph);
}

activity_main.xml:

<?xml version = "1.0" encoding = "utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 
  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:layout_width = "match_parent"
  android:layout_height = "match_parent"
  tools:context = ".MainActivity">

  <!-- Following line omitted inside <fragment> -->
  <!-- app:navGraph = "@navigation/navigation_main" -->
  <fragment
    android:id = "@+id/fragment_main_nav_host"
    android:layout_width = "match_parent"
    android:layout_height = "match_parent"
    android:name = "androidx.navigation.fragment.NavHostFragment"
  />

</androidx.constraintlayout.widget.ConstraintLayout>

navigation_main.xml:

<?xml version = "1.0" encoding = "utf-8"?>
<!-- Following line omitted inside <navigation>-->
<!-- app:startDestination = "@id/oneFragment" -->
<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/navigation_main"
  >

  <fragment
    android:id = "@+id/oneFragment"
    android:name = "com.apponymous.apponymous.OneFragment"
    android:label = "fragment_one"
    tools:layout = "@layout/fragment_one"/>
  <fragment
    android:id = "@+id/twoFragment"
    android:name = "com.apponymous.apponymous.TwoFragment"
    android:label = "fragment_two"
    tools:layout = "@layout/fragment_two"/>
</navigation>

Что, если для этого startDestination нужен пакет?

Damien Locque 30.01.2019 15:14

Как мне вернуться с twoFragment обратно к oneFragment, удалив twoFragment из заднего стека? Я имею в виду, что API навигации эквивалент getSupportFragmentManager().beginTransaction().replace(R.id.‌​nav_host_fragment, new OneFragment()).commit();? Также не могли бы вы показать, где поставить setSupportActionBar() и NavigationUI.setupWithNavController()? Спасибо ?

Oliver 05.02.2019 12:15

Что, если startDestination нужен пакет?

Manikanta 25.03.2019 11:41

@DamienLocque @Manikanta использует navController.setGraph(graph, bundle)

amitavk 12.04.2019 13:08

используйте navController.setGraph (graph, intent.extras)

Dino Sunny 17.07.2020 07:58

Это можно сделать с помощью действия навигации. Поскольку fragmentA - это ваш начальный пункт назначения, определите действие в fragmentA.

Обратите внимание на эти два поля: app:popUpToInclusive = "true" app:popUpTo = "@id/fragmentA"

<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/lrf_navigation"
   app:startDestination = "@id/fragmentA">

   <fragment
       android:id = "@+id/fragmentA"
       android:name = "com.mindinventory.FragmentA"
       android:label = "fragment_a"
       tools:layout = "@layout/fragment_a">

    <action android:id = "@+id/action_a_to_b"
            app:destination = "@id/fragmentB"
            app:popUpToInclusive = "true"
            app:popUpTo = "@id/fragmentA"/>
   <fragment>

   <fragment
       android:id = "@+id/fragmentB"
       android:name = "com.mindinventory.FragmentB"
       android:label = "fragment_b"
       tools:layout = "@layout/fragment_b"/>
</navigation>

Когда ваша MainActivity запустится, просто выполните навигацию с идентификатором действия, он удалит фрагмент A из стека и перейдет к фрагменту B. Похоже, что fragmentB - ваша отправная точка.

if (!isAllSetUp)
{
  // FragmentB
  navController.navigate(R.id.action_a_to_b)
}

Спасибо, красивое чистое решение! Вы также можете, по-видимому, сделать это в onCreate фрагмента. Я делаю это там, потому что я также хочу, чтобы это происходило также, когда фрагмент вызывается из меню ящика в моем случае.

Max 28.01.2020 18:38

Хотя navigate () не уничтожает фрагмент сразу (даже если он удаляется из backstack в процессе). Итак, в onViewCreated мое приложение действительно вылетело из строя. Я исправил это, но это кажется уродливым взломом для раннего выхода из onViewCreated, когда он ушел.

Max 29.01.2020 17:18

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

Я всегда показываю заставку. Он определит, какой экран будет показан в следующий раз.

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

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

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

Чтобы вернуть 1 фрагмент и перейти к другому, вы можете использовать следующий код:

val nextDestination = if (loginSuccess) {
        R.id.action_Dashboard
    } else {
        R.id.action_NotAuthorized
    }

val options = NavOptions.Builder()
        .setPopUpTo(R.id.loginParentFragment, true)
        .build()

findNavController().navigate(nextDestination, null, options)

Если вы сделаете глубокую ссылку из уведомления, ваш backstack также будет содержать заставку.

uberchilly 24.08.2020 23:44

это не ответ, а просто репликация ответа @Akash Patel более чистым и понятным способом.

// in your MainActivity
navController = findNavController(R.id.nav_host_fragment)
val graph = navController.navInflater.inflate(R.navigation.nav_graph)
if (Authentication.checkUserLoggedIn()) {
      graph.startDestination = R.id.homeFragment
} else {
      graph.startDestination = R.id.loginFragment
}
navController.graph = graph

Если вы воспользуетесь этим решением, вы получите сбой, если действия больше не существуют. java.lang.IllegalStateException: unknown destination during restore: com.x:id/navigation_on_boarding

rafaelasguerra 05.12.2019 13:52

ты пробовал? Я уже использую его, и у него вообще нет побочных эффектов, не могли бы вы объяснить больше о том, что вы уже пробовали, и привести к появлению illegalStateException, чтобы я мог проверить его на своей настройке и вернуться к вам с отзывами

Bahaa KA 05.12.2019 16:05

Другие вопросы по теме