Próbuję użyć nowego komponentu nawigacji. Używam BottomNavigationView z navController: NavigationUI.setupWithNavController (bottomNavigation, navController)
Ale kiedy zmieniam fragmenty, za każdym razem niszczą / tworzą, nawet jeśli były wcześniej używane.
Czy istnieje sposób, aby utrzymać przy życiu nasze główne fragmenty, które prowadzą do naszego widoku BottomNavigationView?
android
navigation
bottomnavigationview
IdAndro
źródło
źródło
Odpowiedzi:
Spróbuj tego.
Nawigator
Utwórz nawigator niestandardowy.
@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
Utwórz niestandardowy NavHostFragment.
class CustomNavHostFragment: NavHostFragment() { override fun onCreateNavController(navController: NavController) { super.onCreateNavController(navController) navController.navigatorProvider += PersistentNavigator(context!!, childFragmentManager, id) } }
navigation.xml
Użyj tagu niestandardowego zamiast tagu fragmentowego.
<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>
układ zajęć
Użyj CustomNavHostFragment zamiast 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>
Aktualizacja
Stworzyłem przykładowy projekt. połączyć
Nie tworzę niestandardowego NavHostFragment. Używam
navController.navigatorProvider += navigator
.źródło
fragments
są ponownie używane, a nie odtwarzane), występuje problem z nawigacją. Każdymenuitem
z nichbottomnavigationview
jest uważany za podstawowy - dlatego poBACK
naciśnięciu aplikacja kończy pracę (lub przechodzi do górnego komponentu). Lepszym rozwiązaniem byłoby zachowywanie się tak, jak w aplikacji YouTube: (1) Dotknij ekranu głównego; (2) Dotknij Trendy; (3) Dotknij Skrzynka odbiorcza; (4) Dotknij Trendy; (5) DotknijBACK
- przechodzi do skrzynki odbiorczej; (6) DotknijBACK
- przechodzi do domu; (7) TapBACK
- aplikacja istnieje. Podsumowując,BACK
funkcja YouTube między „menuitems
” wraca do najnowszej, bez powtarzania elementów.app:navGraph
ze swojej aktywności element NavHostFragment.Link do próbek Google Po prostu skopiuj NavigationExtensions do swojej aplikacji i skonfiguruj według przykładu. Działa świetnie.
źródło
Po wielu godzinach poszukiwań znalazłem rozwiązanie. Cały czas było tuż przed nami :) Jest funkcja:
popBackStack(destination, inclusive)
która nawiguje do podanego celu, jeśli znajdzie się w backStack. Zwraca wartość logiczną, więc możemy nawigować tam ręcznie, jeśli kontroler nie znajdzie fragmentu.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) }
źródło
Jeśli masz problemy z przekazywaniem argumentów, dodaj:
fragment.arguments = args
w klasie
KeepStateNavigator
źródło
Obecnie niedostępne.
Aby obejść ten problem, możesz przechowywać wszystkie pobrane dane w modelu widoku i mieć te dane łatwo dostępne podczas ponownego tworzenia fragmentu. Upewnij się, że otrzymujesz widok, używając kontekstu działań.
Możesz użyć LiveData, aby obserwować cykl życia danych
źródło
Skorzystałem z linku dostarczonego przez @STAR_ZERO i działa dobrze. Dla tych, którzy mają problem z przyciskiem Wstecz, możesz sobie z tym poradzić w hoście aktywności / nawigacji w ten sposób.
override fun onBackPressed() { if(navController.currentDestination!!.id!=R.id.homeFragment){ navController.navigate(R.id.homeFragment) }else{ super.onBackPressed() } }
Po prostu sprawdź, czy aktualnym miejscem docelowym jest Twój fragment główny / domowy (zwykle pierwszy w dolnym widoku nawigacji), jeśli nie, po prostu przejdź z powrotem do fragmentu, jeśli tak, wyjdź z aplikacji lub zrób, co chcesz.
Przy okazji, to rozwiązanie musi współpracować z powyższym linkiem do rozwiązania dostarczonym przez STAR_ZERO, używając keep_state_fragment .
źródło
Rozwiązanie dostarczone przez @ piotr-prus pomogło mi, ale musiałem dodać trochę aktualnego sprawdzenia miejsca docelowego:
if (navController.currentDestination?.id == resId) { return //do not navigate }
bez tego sprawdzenia aktualny cel zostanie odtworzony, jeśli przez pomyłkę do niego przejdziesz, ponieważ nie zostałby znaleziony na tylnym stosie.
źródło
Zgodnie z Androidem Dokumentacji ,
Zmień nav_host_fragment na -> FragmentContainerView i pobierz navController
val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment val navController = navHostFragment.navController
Korzystając z tego podejścia, można zachować stan fragmentu za pomocą składników nawigacji
źródło