Transmettre des données entre les destinations

Navigation vous permet d'associer des données à une opération de navigation en définissant des arguments pour une destination donnée. Par exemple, une destination de profil utilisateur peut utiliser un argument d'ID utilisateur pour déterminer l'utilisateur à afficher.

En général, il est fortement préférable de ne transmettre que la quantité minimale de données entre les destinations. Par exemple, vous devez transmettre une clé pour récupérer un objet plutôt que de transmettre l'objet lui-même, car l'espace total pour tous les états enregistrés est limité sur Android. Si vous devez transmettre de grandes quantités de données, utilisez un ViewModel comme décrit dans la section Présentation de ViewModel.

Définir des arguments de destination

Pour transmettre des données entre les destinations, commencez par définir l'argument en l'ajoutant à la destination qui le reçoit. Procédez ainsi :

  1. Dans l'éditeur de navigation, cliquez sur la destination qui reçoit l'argument.
  2. Dans le panneau Attributes (Attributs), cliquez sur Add (+) (Ajouter).
  3. Dans la fenêtre Add Argument Link (Ajouter un lien d'argument) qui s'affiche, saisissez le nom de l'argument, son type, indiquez que la valeur est nullable et une valeur par défaut si nécessaire.
  4. Cliquez sur Add (Ajouter). Notez que l'argument apparaît maintenant dans la liste Arguments du panneau Attributes (Attributs).
  5. Cliquez ensuite sur l'action correspondante pour accéder à cette destination. Dans le panneau Attributes (Attributs), l'argument que vous venez d'ajouter doit maintenant apparaître dans la section Argument Default Values (Valeurs par défaut des arguments).
  6. Vous pouvez également voir que l'argument a été ajouté en XML. Cliquez sur l'onglet Text (Texte) pour passer à la vue XML. Notez que votre argument a été ajouté à la destination qui le reçoit. Voici un exemple :

     <fragment android:id="@+id/myFragment" >
         <argument
             android:name="myArg"
             app:argType="integer"
             android:defaultValue="0" />
     </fragment>
    

Types d'arguments pris en charge

La bibliothèque Navigation accepte les types d'arguments suivants :

Type Syntaxe app:argType Compatibilité avec les valeurs par défaut Géré par des routes Nullable
Nombre entier app:argType="integer" Oui Oui Non
Float app:argType="float" Oui Oui Non
Long app:argType="long" Oui. Les valeurs par défaut doivent toujours se terminer par le suffixe "L" (par exemple, "123L"). Oui Non
Booléen app:argType="boolean" Oui. "true" ou "false" Oui Non
Chaîne app:argType="string" Oui Oui Oui
Référence de ressource app:argType="reference" Oui. Les valeurs par défaut doivent se présenter sous la forme "@resourceType/resourceName" (par exemple, "@style/myCustomStyle") ou "0". Oui Non
Parcelable personnalisé app:argType="<type>", où <type> est le nom complet de la classe de l'élément Parcelable Accepte la valeur par défaut "@null". Incompatible avec les autres valeurs par défaut. Non Oui
Sérialisable personnalisé app:argType="<type>", où <type> est le nom complet de la classe de l'élément Serializable Accepte la valeur par défaut "@null". Incompatible avec les autres valeurs par défaut. Non Oui
Énumération personnalisée app:argType="<type>", où <type> est le nom complet de l'énumération Oui. Les valeurs par défaut doivent correspondre au nom non qualifié (par exemple, "SUCCESS" pour correspondre à MyEnum.SUCCESS). Non Non

Si un type d'argument accepte les valeurs nulles, vous pouvez déclarer une valeur par défaut "null" en indiquant android:defaultValue="@null".

Les routes, les liens profonds et les URI avec leurs arguments peuvent être analysés à partir de chaînes. Cela n'est pas possible en utilisant des types de données personnalisés tels que des parcelables et des sérialisables, comme indiqué dans le tableau ci-dessus. Pour transmettre des données complexes personnalisées, stockez les données ailleurs (un ViewModel ou une base de données, par exemple) et ne transmettez un identifiant que pendant la navigation. Ensuite, récupérez les données au nouvel emplacement une fois la navigation terminée.

Lorsque vous choisissez l'un des types personnalisés, la boîte de dialogue Select Class (Sélectionner une classe) s'affiche et vous invite à choisir la classe correspondante pour ce type. L'onglet Project (Projet) vous permet de choisir une classe à partir de votre projet actuel.

Vous pouvez sélectionner <inferred type> pour que la bibliothèque Navigation détermine le type en fonction de la valeur fournie.

Vous pouvez cocher Array (Tableau) pour indiquer que l'argument doit être un tableau de la valeur Type sélectionnée. Remarques :

  • Les tableaux d'énumérations et de références de ressources ne sont pas pris en charge.
  • Les tableaux prennent en charge les valeurs nullables, quelles que soient les variables potentiellement nulles du type sous-jacent Par exemple, l'utilisation de app:argType="integer[]" vous permet d'utiliser app:nullable="true" pour indiquer qu'il est acceptable de transmettre un tableau nul.
  • Les tableaux ne prennent en charge qu'une seule valeur par défaut, à savoir "@null", Les tableaux n'acceptent aucune autre valeur par défaut.

Remplacer un argument de destination dans une action

Les arguments et les valeurs par défaut au niveau de la destination sont utilisés par toutes les actions qui permettent d'y accéder. Si nécessaire, vous pouvez remplacer la valeur par défaut d'un argument (ou en définir une, si ce n'est pas déjà fait) en définissant un argument au niveau de l'action. Celui-ci doit avoir le même nom et le même type que l'argument déclaré dans la destination.

Le code XML suivant reprend une action avec un argument qui force l'argument au niveau de la destination de l'exemple précédent :

<action android:id="@+id/startMyFragment"
    app:destination="@+id/myFragment">
    <argument
        android:name="myArg"
        app:argType="integer"
        android:defaultValue="1" />
</action>

Utiliser Safe Args pour transmettre des données avec sûreté du typage

Le composant Navigation dispose d'un plug-in Gradle appelé "Safe Args". Celui-ci génère des classes d'objets et de compilateurs simples pour une navigation sécurisée et l'accès à tous les arguments associés. Safe Args est vivement recommandé pour la navigation et la transmission de données, car il garantit la sûreté du typage.

Si vous n'utilisez pas Gradle, vous ne pouvez pas utiliser le plug-in Safe Args. Vous pouvez alors utiliser des bundles pour transmettre directement des données.

如需将 Safe Args 添加到您的项目,请在顶层 build.gradle 文件中包含以下 classpath

Groovy

buildscript {
    repositories {
        google()
    }
    dependencies {
        def nav_version = "2.8.0"
        classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version"
    }
}

Kotlin

buildscript {
    repositories {
        google()
    }
    dependencies {
        val nav_version = "2.8.0"
        classpath("androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version")
    }
}

您还必须应用以下两个可用插件之一。

如需生成适用于 Java 模块或 Java 和 Kotlin 混合模块的 Java 语言代码,请将以下行添加到应用或模块build.gradle 文件中:

Groovy

plugins {
  id 'androidx.navigation.safeargs'
}

Kotlin

plugins {
    id("androidx.navigation.safeargs")
}

此外,如需生成仅适用于 Kotlin 模块的 Kotlin 语言代码,请添加以下行:

Groovy

plugins {
  id 'androidx.navigation.safeargs.kotlin'
}

Kotlin

plugins {
    id("androidx.navigation.safeargs.kotlin")
}

根据迁移到 AndroidX 文档,您的 gradle.properties 文件中必须具有 android.useAndroidX=true

Une fois que vous avez activé Safe Args, le code généré contient les classes et méthodes sûres suivantes pour chaque action, ainsi que pour chaque destination d'envoi et de réception.

  • Une classe est créée pour chaque destination à l'origine d'une action. Le nom de cette classe correspond au nom de la destination d'origine, auquel est ajouté le mot "Directions". Par exemple, si la destination d'origine est un fragment nommé SpecifyAmountFragment, la classe générée est appelée SpecifyAmountFragmentDirections.

    Cette classe dispose d'une méthode pour chaque action définie dans la destination d'origine.

  • Pour chaque action utilisée afin de transmettre l'argument, une classe interne est créée, et son nom est basé sur celle-ci. Par exemple, si l'action est appelée confirmationAction,, la classe est nommée ConfirmationAction. Si votre action contient des arguments sans defaultValue, vous définissez la valeur des arguments à l'aide de la classe d'action associée.

  • Une classe est créée pour la destination de réception. Le nom de cette classe est celui de la destination, suivi du mot "Args". Par exemple, si le fragment de destination est ConfirmationFragment,, la classe générée est nommée ConfirmationFragmentArgs. Utilisez la méthode fromBundle() de cette classe pour récupérer les arguments.

L'exemple suivant montre comment utiliser ces méthodes pour définir un argument et le transmettre à la méthode navigate() :

Kotlin

override fun onClick(v: View) {
   val amountTv: EditText = view!!.findViewById(R.id.editTextAmount)
   val amount = amountTv.text.toString().toInt()
   val action = SpecifyAmountFragmentDirections.confirmationAction(amount)
   v.findNavController().navigate(action)
}

Java

@Override
public void onClick(View view) {
   EditText amountTv = (EditText) getView().findViewById(R.id.editTextAmount);
   int amount = Integer.parseInt(amountTv.getText().toString());
   ConfirmationAction action =
           SpecifyAmountFragmentDirections.confirmationAction();
   action.setAmount(amount);
   Navigation.findNavController(view).navigate(action);
}

Dans le code de la destination de réception, utilisez la méthode getArguments() pour récupérer le bundle et utiliser son contenu. Lorsqu'ils utilisent les dépendances -ktx, les utilisateurs de Kotlin peuvent également utiliser le délégué de propriété by navArgs() pour accéder aux arguments.

Kotlin

val args: ConfirmationFragmentArgs by navArgs()

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    val tv: TextView = view.findViewById(R.id.textViewAmount)
    val amount = args.amount
    tv.text = amount.toString()
}

Java

@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
    TextView tv = view.findViewById(R.id.textViewAmount);
    int amount = ConfirmationFragmentArgs.fromBundle(getArguments()).getAmount();
    tv.setText(amount + "");
}

Utiliser Safe Args avec une action globale

Lorsque vous utilisez Safe Args avec une action globale, vous devez fournir une valeur android:id pour votre élément <navigation> racine, comme dans cet exemple :

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

    ...

</navigation>

La navigation génère une classe Directions pour l'élément <navigation> basée sur la valeur android:id. Par exemple, si vous disposez d'un élément <navigation> avec android:id=@+id/main_nav, la classe générée est appelée MainNavDirections. Toutes les destinations de l'élément <navigation> ont généré des méthodes pour accéder à l'ensemble des actions globales associées à l'aide des mêmes méthodes que celles décrites dans la section précédente.

Transmettre des données entre des destinations avec des objets Bundle

Si vous n'utilisez pas Gradle, vous pouvez toujours transmettre des arguments entre les destinations à l'aide d'objets Bundle. Créez un objet Bundle et transmettez-le à la destination à l'aide de navigate(), comme dans l'exemple suivant :

Kotlin

val bundle = bundleOf("amount" to amount)
view.findNavController().navigate(R.id.confirmationAction, bundle)

Java

Bundle bundle = new Bundle();
bundle.putString("amount", amount);
Navigation.findNavController(view).navigate(R.id.confirmationAction, bundle);

Dans le code de votre destination, utilisez la méthode getArguments() pour récupérer l'objet Bundle et utiliser son contenu :

Kotlin

val tv = view.findViewById<TextView>(R.id.textViewAmount)
tv.text = arguments?.getString("amount")

Java

TextView tv = view.findViewById(R.id.textViewAmount);
tv.setText(getArguments().getString("amount"));

Transmettre les données à la destination de départ

Vous pouvez transmettre des données à la destination de départ de votre application. Tout d'abord, vous devez construire explicitement un objet Bundle contenant les données. Ensuite, utilisez l'une des approches suivantes pour transmettre l'élément Bundle à la destination de départ :

Pour récupérer les données de votre destination de départ, appelez Fragment.getArguments().

Remarques concernant ProGuard

Si vous réduisez votre code, vous devez empêcher l'obscurcissement des noms de classe Parcelable, Serializable et Enum lors du processus de minimisation. Pour cela, deux possibilités s'offrent à vous :

  • Utiliser les annotations @Keep
  • Utiliser des règles keepnames

Ces approches sont décrites dans les sous-sections suivantes.

Utiliser les annotations @Keep

L'exemple suivant ajoute des annotations @Keep aux définitions de classe de modèle :

Kotlin

@Keep class ParcelableArg : Parcelable { ... }

@Keep class SerializableArg : Serializable { ... }

@Keep enum class EnumArg { ... }

Java

@Keep public class ParcelableArg implements Parcelable { ... }

@Keep public class SerializableArg implements Serializable { ... }

@Keep public enum EnumArg { ... }

Utiliser des règles keepnames

Vous pouvez également ajouter des règles keepnames à votre fichier proguard-rules.pro, comme dans l'exemple suivant :

proguard-rules.pro

...

-keepnames class com.path.to.your.ParcelableArg
-keepnames class com.path.to.your.SerializableArg
-keepnames class com.path.to.your.EnumArg

...

Ressources supplémentaires

Pour en savoir plus sur la navigation, consultez les ressources supplémentaires suivantes.

Exemples

Ateliers de programmation

Vidéos