J'essaye 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 étaient précédemment utilisés.

Existe-t-il un moyen de maintenir en vie le lien de nos principaux fragments vers notre BottomNavigationView?

79
IdAndro 23 mai 2018 à 13:28

7 réponses

Meilleure réponse

Essaye ça.

Navigatrice

Créez un navigateur personnalisé.

@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éez NavHostFragment personnalisé.

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

Navigation.xml

Utilisez une balise personnalisée au lieu de la balise fragment.

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

Disposition de l'activité

Utilisez 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éé un exemple de projet. lien

Je ne crée pas de NavHostFragment personnalisé. J'utilise navController.navigatorProvider += navigator.

54
Link182 3 sept. 2019 à 14:31

Non disponible pour le moment.

Pour contourner le problème, vous pouvez stocker toutes vos données extraites dans le modèle de vue et disposer de ces données en lecture seule 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 observables en fonction du cycle de vie

0
Samuel Robert 20 sept. 2018 à 13:09

Lien d'exemples Google Copiez simplement NavigationExtensions dans votre application et configurez par exemple. Fonctionne très bien.

17
Zakhar Rodionov 22 mars 2019 à 11:54

Après de nombreuses 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 Boolean, 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)
    }
9
Piotr Prus 29 mars 2019 à 14:55

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

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

Sans cette vérification, la destination actuelle sera recréée si vous y accédez par erreur, car elle ne se trouverait pas dans la pile arrière.

1
akhris 29 juil. 2019 à 05:55

J'ai utilisé le lien fourni par @STAR_ZERO et cela fonctionne très bien. Pour ceux qui ont des problèmes avec le bouton de retour, vous pouvez le gérer dans l'hôte activité / nav comme ceci.

override fun onBackPressed() {
        if(navController.currentDestination!!.id!=R.id.homeFragment){
            navController.navigate(R.id.homeFragment)
        }else{
            super.onBackPressed()
        }
    }

Vérifiez simplement si la destination actuelle est votre fragment racine / d'accueil (normalement le premier dans la vue de navigation inférieure), sinon, revenez simplement au fragment, si oui, quittez uniquement l'application ou faites ce que vous voulez.

Btw, cette solution doit fonctionner avec le lien de solution ci-dessus fourni par STAR_ZERO, en utilisant keep_state_fragment .

1
veeyikpong 27 avril 2019 à 06:04

Si vous avez du mal à passer des arguments, ajoutez:

fragment.arguments = args

En classe KeepStateNavigator

0
Victorlopesjg 17 oct. 2019 à 16:07