Я пытаюсь использовать новый компонент навигации. Я использую BottomNavigationView с navController: NavigationUI.setupWithNavController (bottomNavigation, navController)
Но когда я переключаю фрагменты, они каждый раз уничтожают / создают, даже если раньше использовались.
Есть ли способ сохранить в действии наши основные фрагменты ссылки на наш BottomNavigationView?
Ответы:
Попробуй это.
Навигатор
Создать собственный навигатор.
@Navigator.Name("custom_fragment") // Use as custom tag at navigation.xml class CustomNavigator( private val context: Context, private val manager: FragmentManager, private val containerId: Int ) : FragmentNavigator(context, manager, containerId) { override fun navigate(destination: Destination, args: Bundle?, navOptions: NavOptions?) { val tag = destination.id.toString() val transaction = manager.beginTransaction() val currentFragment = manager.primaryNavigationFragment if (currentFragment != null) { transaction.detach(currentFragment) } var fragment = manager.findFragmentByTag(tag) if (fragment == null) { fragment = destination.createFragment(args) transaction.add(containerId, fragment, tag) } else { transaction.attach(fragment) } transaction.setPrimaryNavigationFragment(fragment) transaction.setReorderingAllowed(true) transaction.commit() dispatchOnNavigatorNavigated(destination.id, BACK_STACK_DESTINATION_ADDED) } }
NavHostFragment
Создайте собственный NavHostFragment.
class CustomNavHostFragment: NavHostFragment() { override fun onCreateNavController(navController: NavController) { super.onCreateNavController(navController) navController.navigatorProvider += PersistentNavigator(context!!, childFragmentManager, id) } }
navigation.xml
Используйте собственный тег вместо тега фрагмента.
<navigation xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/navigation" app:startDestination="@id/navigation_first"> <custom_fragment android:id="@+id/navigation_first" android:name="com.example.sample.FirstFragment" android:label="FirstFragment" /> <custom_fragment android:id="@+id/navigation_second" android:name="com.example.sample.SecondFragment" android:label="SecondFragment" /> </navigation>
макет деятельности
Используйте CustomNavHostFragment вместо NavHostFragment.
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/container" android:layout_width="match_parent" android:layout_height="match_parent"> <fragment android:id="@+id/nav_host_fragment" android:name="com.example.sample.CustomNavHostFragment" android:layout_width="0dp" android:layout_height="0dp" app:layout_constraintBottom_toTopOf="@+id/bottom_navigation" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:navGraph="@navigation/navigation" /> <com.google.android.material.bottomnavigation.BottomNavigationView android:id="@+id/bottom_navigation" android:layout_width="0dp" android:layout_height="wrap_content" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:menu="@menu/navigation" /> </androidx.constraintlayout.widget.ConstraintLayout>
Обновить
Я создал образец проекта. ссылка на сайт
Я не создаю собственный NavHostFragment. Пользуюсь
navController.navigatorProvider += navigator
.источник
fragments
используются повторно, а не воссоздаются), есть проблема с навигацией. Каждыйmenuitem
вbottomnavigationview
считается первичным - таким образом, приBACK
нажатии на приложение заканчивается (или переходит к началу компонента). Лучшим решением было бы вести себя так же, как приложение YouTube: (1) Нажмите «Домой»; (2) Нажмите «Тенденции»; (3) Нажмите «Входящие»; (4) Нажмите «Тенденции»; (5) НажмитеBACK
- переход во Входящие; (6) ТапBACK
- перейти на главную; (7) TapBACK
- приложение существует. Таким образом,BACK
функциональность YouTube между "menuitems
" восходит к самому последнему, без повторения элементов.app:navGraph
NavHostFragment из своей активности.Ссылка на образцы Google. Просто скопируйте NavigationExtensions в свое приложение и настройте на примере. Работает отлично.
источник
После многих часов исследований я нашел решение. Это все время было прямо перед нами :) Есть функция:
popBackStack(destination, inclusive)
которая перемещается к заданному месту назначения, если найдена в backStack. Он возвращает логическое значение, поэтому мы можем перемещаться туда вручную, если контроллер не найдет фрагмент.if(findNavController().popBackStack(R.id.settingsFragment, false)) { Log.d(TAG, "SettingsFragment found in backStack") } else { Log.d(TAG, "SettingsFragment not found in backStack, navigate manually") findNavController().navigate(R.id.settingsFragment) }
источник
Если у вас возникли проблемы с передачей аргументов, добавьте:
fragment.arguments = args
в классе
KeepStateNavigator
источник
На данный момент недоступно.
В качестве обходного пути вы можете сохранить все выбранные вами данные в модели представления и сделать эти данные доступными при воссоздании фрагмента. Убедитесь, что вы получаете представление с использованием контекста действий.
Вы можете использовать LiveData, чтобы сделать ваши данные наблюдаемыми с учетом жизненного цикла
источник
Я использовал ссылку, предоставленную @STAR_ZERO, и она отлично работает. Для тех, у кого проблема с кнопкой возврата, вы можете справиться с этим в хосте activity / nav следующим образом.
override fun onBackPressed() { if(navController.currentDestination!!.id!=R.id.homeFragment){ navController.navigate(R.id.homeFragment) }else{ super.onBackPressed() } }
Просто проверьте, является ли текущий пункт назначения вашим корневым / домашним фрагментом (обычно первым в нижнем навигационном представлении), если нет, просто перейдите к фрагменту, если да, только выйдите из приложения или делайте все, что хотите.
Кстати, это решение должно работать вместе со ссылкой на решение выше, предоставленной STAR_ZERO, с использованием keep_state_fragment .
источник
Решение, предоставленное @ piotr-prus, помогло мне, но мне пришлось добавить некоторую проверку текущего пункта назначения:
if (navController.currentDestination?.id == resId) { return //do not navigate }
без этой проверки текущий пункт назначения будет воссоздан, если вы по ошибке перейдете к нему, потому что он не будет найден в заднем стеке.
источник
Согласно документации Android ,
Измените свой nav_host_fragment на -> FragmentContainerView и извлеките navController
val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment val navController = navHostFragment.navController
Вы можете сохранить состояние фрагмента с помощью компонентов навигации, используя этот подход.
источник