Я использую Навигация из 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?
проверьте это stackoverflow.com/questions/51173002/…
Привет @NileshRathod! Это сработает, если я сначала открою FragmentA, а затем FragmentB. но я хочу программно установить StartDestination. У вас есть какое-нибудь решение?
Попробуй этот NavigationUI.onNavDestinationSelected(menuItem, navController.getNavController());
Я хочу сделать вышеперечисленное без меню или любого другого элемента управления навигацией. возможно, нет возможности установить startDestination условно. так что на данный момент я использовал [stackoverflow.com/questions/51173002/…, как вы предложили.
В этот ответ добавлено динамическое расширение: Динамическое изменение пункта назначения
Наконец-то я получил ответ на свой запрос ...
Поместите ниже код в метод 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.
Я бы также добавил, что для сохранения поведения кнопки возврата в коде Kotlin вам все равно нужен способ установить navHostFragment в качестве хоста навигации по умолчанию. Это можно сделать с помощью этой транзакции: supportFragmentManager.beginTransaction().setPrimaryNavigationFragment(navHostFragment).commit()
метода graph.setDefaultArguments () больше не существует, вам также не нужны последние 2 строки (NavigationUI). См. мой ответ для получения обновленной версии.
Можно ли добавить анимацию перехода при настройке графика?
Будет ли работать глубинная ссылка, поскольку она добавляет startDestination, когда вы переходите к глубокому пункту назначения? Что он добавит с этой настройкой?
На самом деле это не работает для меня. Ответ @ DanishKhan имеет значение.
Некоторые 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 нужен пакет?
Как мне вернуться с twoFragment обратно к oneFragment, удалив twoFragment из заднего стека? Я имею в виду, что API навигации эквивалент getSupportFragmentManager().beginTransaction().replace(R.id.nav_host_fragment, new OneFragment()).commit();? Также не могли бы вы показать, где поставить setSupportActionBar() и NavigationUI.setupWithNavController()? Спасибо ?
Что, если startDestination нужен пакет?
@DamienLocque @Manikanta использует navController.setGraph(graph, bundle)
используйте navController.setGraph (graph, intent.extras)
Это можно сделать с помощью действия навигации. Поскольку 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 фрагмента. Я делаю это там, потому что я также хочу, чтобы это происходило также, когда фрагмент вызывается из меню ящика в моем случае.
Хотя navigate () не уничтожает фрагмент сразу (даже если он удаляется из backstack в процессе). Итак, в onViewCreated мое приложение действительно вылетело из строя. Я исправил это, но это кажется уродливым взломом для раннего выхода из onViewCreated, когда он ушел.
Вы можете установить начальную точку назначения программно, однако большую часть времени ваша стартовая логика будет обращаться к какой-либо удаленной конечной точке. Если вы ничего не показываете на экране, ваш интерфейс будет плохо выглядеть.
Я всегда показываю заставку. Он определит, какой экран будет показан в следующий раз.
Например, на картинке выше вы можете спросить в состоянии экрана-заставки, есть ли сохраненный 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 также будет содержать заставку.
это не ответ, а просто репликация ответа @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
ты пробовал? Я уже использую его, и у него вообще нет побочных эффектов, не могли бы вы объяснить больше о том, что вы уже пробовали, и привести к появлению illegalStateException, чтобы я мог проверить его на своей настройке и вернуться к вам с отзывами
# навигационный-архитектурный-компонент # навигационный-граф