143 votes

Existe-t-il un moyen de conserver le fragment vivant lors de l'utilisation de BottomNavigationView avec le nouveau NavController?

J'essaie d'utiliser le nouveau composant de navigation. J'utilise un BottomNavigationView avec le navController: NavigationUI.setupWithNavController (bottomNavigation, navController)

Mais quand je change de fragments, ils sont à chaque fois détruits / créés même s'ils ont été utilisés précédemment.

Existe-t-il un moyen de maintenir en vie nos principaux fragments liés à notre BottomNavigationView?

83voto

STAR_ZERO Points 554

Essayez ceci.

Navigator

Créer des navigateur.

@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

Créer des NavHostFragment.

class CustomNavHostFragment: NavHostFragment() {
    override fun onCreateNavController(navController: NavController) {
        super.onCreateNavController(navController)
        navController.navigatorProvider += PersistentNavigator(context!!, childFragmentManager, id)
    }
}

navigation.xml

L'utilisation de balises personnalisées au lieu de fragment de balise.

<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>

l'activité de mise en page

Utilisation CustomNavHostFragment au lieu 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>

Mise à jour

J'ai créé des exemples de projet. lien

Je n'ai pas de créer des NavHostFragment. J'utilise navController.navigatorProvider += navigator.

37voto

Zakhar Rodionov Points 150

Lien d'exemples Google Il suffit de copier les extensions de navigation dans votre application et de configurer par exemple. Fonctionne très bien.

17voto

Piotr Prus Points 21

Après plusieurs heures de recherche, j'ai trouvé une solution. C'était tout le temps juste devant nous :) Il y a une fonction: popBackStack(destination, inclusive) qui navigue vers une destination donnée si elle est trouvée dans backStack. Il renvoie un booléen, nous pouvons donc y naviguer manuellement si le contrôleur ne trouve pas le fragment.

 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)
    }
 

1voto

Samuel Robert Points 2755

Non disponible pour l'instant.

Comme solution de contournement, vous pouvez stocker toutes vos données extraites dans le modèle de vue et les rendre disponibles en lecture lorsque vous recréez le fragment. Assurez-vous d'obtenir la vue en utilisant le contexte des activités.

Vous pouvez utiliser LiveData pour rendre vos données sensibles au cycle de vie observables

0voto

akhris Points 76

La solution fournie par @ piotr-prus m'a aidé, mais j'ai dû ajouter une vérification de la destination actuelle:

 if (navController.currentDestination?.id == resId) {
    return       //do not navigate
}
 

sans cette vérification, la destination actuelle va être recréée si vous y accédez par erreur, car elle ne serait pas trouvée dans la pile arrière.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X