Estoy intentando utilizar el nuevo componente de navegación. Yo uso un BottomNavigationView con navController: NavigationUI.setupWithNavController (bottomNavigation, navController)
Pero cuando cambio fragmentos, cada vez se destruyen / crean incluso si se usaron previamente.
¿Hay alguna manera de mantener vivo nuestro enlace de fragmentos principales a nuestro BottomNavigationView?
android
navigation
bottomnavigationview
IdAndro
fuente
fuente
Respuestas:
Prueba esto.
Navegador
Crea un navegador personalizado.
@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
Cree NavHostFragment personalizado.
class CustomNavHostFragment: NavHostFragment() { override fun onCreateNavController(navController: NavController) { super.onCreateNavController(navController) navController.navigatorProvider += PersistentNavigator(context!!, childFragmentManager, id) } }
navigation.xml
Utilice una etiqueta personalizada en lugar de una etiqueta de fragmento.
<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>
diseño de actividad
Utilice CustomNavHostFragment en lugar de 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>
Actualizar
Creé un proyecto de muestra. enlace
No creo NavHostFragment personalizado. Yo uso
navController.navigatorProvider += navigator
.fuente
fragments
se reutiliza, no se vuelve a crear), hay un problema con la navegación. Cadamenuitem
enbottomnavigationview
se considera primaria - por lo tanto, cuandoBACK
se presiona, los acabados aplicación (o va a la componente de la parte superior). Una mejor solución sería comportarse como la aplicación de YouTube: (1) Toque Inicio; (2) Toque Tendencias; (3) Toque Bandeja de entrada; (4) Toque Tendencias; (5) ToqueBACK
: va a la Bandeja de entrada; (6) ToqueBACK
- va a Inicio; (7) TapBACK
: la aplicación existe. En resumen, laBACK
funcionalidad de YouTube entre "menuitems
" se remonta a la más reciente, sin elementos repetidos.app:navGraph
de NavHostFragment de su actividad.Vínculo de ejemplos de Google Simplemente copie NavigationExtensions en su aplicación y configúrelo con un ejemplo. Funciona genial.
fuente
Después de muchas horas de investigación encontré una solución. Estuvo todo el tiempo justo frente a nosotros :) Hay una función:
popBackStack(destination, inclusive)
que navega a un destino dado si se encuentra en backStack. Devuelve booleano, por lo que podemos navegar allí manualmente si el controlador no encuentra el fragmento.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) }
fuente
Si tiene problemas para pasar argumentos, agregue:
fragment.arguments = args
en la clase
KeepStateNavigator
fuente
No disponible a partir de ahora.
Como solución alternativa, puede almacenar todos los datos obtenidos en el modelo de vista y tener esos datos disponibles cuando vuelva a crear el fragmento. Asegúrese de obtener la vista utilizando el contexto de actividades.
Puede usar LiveData para hacer que sus datos sean observables y conscientes del ciclo de vida
fuente
He utilizado el enlace proporcionado por @STAR_ZERO y funciona bien. Para aquellos que tienen problemas con el botón Atrás, pueden manejarlo en el host de actividad / navegación de esta manera.
override fun onBackPressed() { if(navController.currentDestination!!.id!=R.id.homeFragment){ navController.navigate(R.id.homeFragment) }else{ super.onBackPressed() } }
Simplemente verifique si el destino actual es su fragmento raíz / hogar (normalmente el primero en la vista de navegación inferior), si no, simplemente navegue de regreso al fragmento, si es así, solo salga de la aplicación o haga lo que quiera.
Por cierto, esta solución debe funcionar junto con el enlace de la solución anterior proporcionado por STAR_ZERO, utilizando keep_state_fragment .
fuente
La solución proporcionada por @ piotr-prus me ayudó, pero tuve que agregar una verificación de destino actual:
if (navController.currentDestination?.id == resId) { return //do not navigate }
sin esta verificación, el destino actual se volverá a crear si navega por error hacia él, porque no se encontrará en la pila de actividades.
fuente
Según la documentación de Android ,
Cambie su nav_host_fragment a -> FragmentContainerView y recupere el navController
val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment val navController = navHostFragment.navController
Puede conservar el estado del fragmento con componentes de navegación utilizando este enfoque
fuente