web-dev-qa-db-fra.com

Étendre un modèle de vue à plusieurs fragments (pas d'activité) à l'aide du composant de navigation

J'utilise le composant de navigation, je veux qu'un modèle de vue soit partagé entre quelques fragments, mais ils doivent être effacés lorsque je laisse les fragments (donc je ne les limite pas à l'activité) J'essaie de prendre la même activité plusieurs fragments approche. J'ai réussi à y parvenir à l'aide de plusieurs hôtes de navigation et à délimiter les fragments à l'aide de getParentFragment, mais cela entraîne simplement plus de problèmes à encapsuler les fragments dans d'autres fragments parents, perdant le bouton de retour fonctionnant de manière transparente et d'autres hacks pour faire fonctionner quelque chose qui devrait être assez simple. Quelqu'un at-il une bonne idée sur la façon d'y parvenir? Je me demandais s'il y avait quelque chose avec getViewModelStore que je pourrais utiliser, étant donné l'image ci-dessous, je veux étendre un modèle de vue pour créerCardFragment2 et l'utiliser dans quoi que ce soit après (addPredictions, editImageFragment et d'autres que je n'ai pas encore ajoutés), mais alors si Je reviens à mainFragment Je veux effacer les modèles de vue.

BTW Je ne peux pas simplement appeler clear sur le magasin de modèles de vue mainFragment car il existe d'autres modèles de vue ici qui ne devraient pas être effacés, je suppose que je veux un moyen de dire à l'hôte de navigation quel devrait être le fragment parent, ce que je ne sais pas. va être une chose, ou un moyen de rendre le modèle de vue nouveau si je navigue depuis mainFragment ou cardPreviewFragment

nav graph

10
martinseal1987

Oui, il est possible d'étendre un viewmodel à un navgraph commençant maintenant par androidx.navigation:*:2.1.0-alpha02. Voir les notes de version ici et un exemple de l'API ici . Tout ce que vous devez donner est le R.id pour votre navgraph. Je trouve cela un peu ennuyeux à utiliser, cependant, car normalement les modèles de vue sont initialisés dans onCreate, ce qui n'est pas possible avec cette portée car le contrôleur nav n'est pas encore garanti d'être défini par votre fragment d'hôte nav ( Je trouve que c'est le cas avec les changements de configuration).

De plus, si vous ne voulez pas que votre mainFragment fasse partie de cette portée, je suggère de le retirer et peut-être d'utiliser un graphique de navigation imbriqué .

6
Alex H

Voici un exemple concret de la réponse acceptée d'Alex H.

Dans votre build.gradle (application)

dependencies {
    def nav_version = "2.1.0"
    implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
}

Exemple de modèle de vue

class MyViewModel : ViewModel() { 
    val name: MutableLiveData<String> = MutableLiveData()
}

Dans votre FirstFlowFragment.kt, définissez

val myViewModel: MyViewModel by navGraphViewModels(R.id.your_nested_nav_id)
myViewModel.name.value = "Cool Name"

Et dans votre SecondFlowFragment.kt définir

val myViewModel: MyViewModel by navGraphViewModels(R.id.your_nested_nav_id)
val name = myViewModel.name.value.orEmpty()
Log.d("tag", "welcome $name!")

Maintenant, le ViewModel est délimité dans ce fragment imbriqué, l'état partagé sera détruit lorsque la navigation imbriquée sera également détruite, pas besoin de les réinitialiser manuellement.

3
Miko Chu

donc quand j'ai posté cela, la fonctionnalité était là mais ne fonctionnait pas tout à fait comme prévu, depuis lors j'utilise maintenant tout le temps et cette question continue d'attirer plus d'attention alors j'ai pensé publier un exemple à jour,

en utilisant

//Navigation
implementation "androidx.navigation:navigation-fragment:2.2.0-rc04"
// Navigation UI
implementation "androidx.navigation:navigation-ui:2.2.0-rc04"

je reçois le propriétaire du magasin de modèles de vue comme celui-ci

private ViewModelStoreOwner getStoreOwner() {

        NavController navController = Navigation
                .findNavController(requireActivity(), R.id.root_navigator_fragment);
        return navController.getViewModelStoreOwner(R.id.root_navigator);
}

im en utilisant l'implémentation de plusieurs fragments d'une activité, mais en utilisant cela, je peux effectivement lier mes modèles de vue aux seuls fragments de portée et avec les nouveaux données en direct vous pouvez même limiter cela aussi

le premier identifiant provient du fragment de graphes nav

<?xml version="1.0" encoding="utf-8"?>
  <FrameLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent"
    xmlns:app="http://schemas.Android.com/apk/res-auto">

    <fragment
      Android:id="@+id/root_navigator_fragment"
      Android:layout_width="match_parent"
      Android:layout_height="match_parent"
      Android:name="androidx.navigation.fragment.NavHostFragment"
      app:defaultNavHost="true"
      app:navGraph="@navigation/root_navigator"/>

  </FrameLayout>

et le second vient de l'id du graphique de navigation

  <?xml version="1.0" encoding="utf-8"?>
  <navigation xmlns:Android="http://schemas.Android.com/apk/res/Android"
    xmlns:app="http://schemas.Android.com/apk/res-auto"
    xmlns:tools="http://schemas.Android.com/tools"
    Android:id="@+id/root_navigator"
    app:startDestination="@id/mainNavFragment">

et vous pouvez l'utiliser comme ça

private void setUpSearchViewModel() {
    searchViewModel = new ViewModelProvider(getStoreOwner()).get(SearchViewModel.class);
}
1
martinseal1987