Trasmettere dati tra destinazioni

La navigazione ti consente di associare dati a un'operazione di navigazione definendo gli argomenti per una destinazione. Ad esempio, una destinazione del profilo utente potrebbe richiedere un argomento ID utente per determinare quale utente visualizzare.

In generale, ti consigliamo vivamente di trasmettere solo la quantità minima di dati tra le destinazioni. Ad esempio, devi passare una chiave per recuperare un oggetto piuttosto che l'oggetto stesso, poiché lo spazio totale per tutti gli stati salvati è limitato su Android. Se devi trasmettere grandi quantità di dati, utilizza un ViewModel come descritto nella panoramica di ViewModel.

Definisci gli argomenti di destinazione

Per passare i dati tra le destinazioni, definisci prima l'argomento aggiungendolo alla destinazione che lo riceve seguendo questi passaggi:

  1. Nell'Editor di navigazione, fai clic sulla destinazione che riceve l'argomento.
  2. Nel riquadro Attributi, fai clic su Aggiungi (+).
  3. Nella finestra Aggiungi link argomento visualizzata, inserisci il nome dell'argomento, il tipo di argomento, se l'argomento è nullable e un valore predefinito, se necessario.
  4. Fai clic su Aggiungi. Tieni presente che l'argomento ora viene visualizzato nell'elenco Argomenti nel riquadro Attributi.
  5. Poi, fai clic sull'azione corrispondente che ti indirizza a questa destinazione. Nel riquadro Attributi, dovresti vedere l'argomento appena aggiunto nella sezione Valori predefiniti dell'argomento.
  6. Puoi anche vedere che l'argomento è stato aggiunto in XML. Fai clic sulla scheda Testo per passare alla visualizzazione XML e noterai che l'argomento è stato aggiunto alla destinazione che lo riceve. Un esempio è riportato di seguito:

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

Tipi di argomenti supportati

La libreria Navigation supporta i seguenti tipi di argomenti:

Digitazione Sintassi di app:argType Supporto dei valori predefiniti Gestito dalle route Nullable
Numero intero app:argType="integer" No
Mobile app:argType="float" No
Lungo app:argType="long" Sì: i valori predefiniti devono sempre terminare con un suffisso "L" (ad es. "123L"). No
Booleano app:argType="boolean" Sì: "true" o "false" No
Stringa app:argType="string"
Riferimento alla risorsa app:argType="reference" Sì: i valori predefiniti devono essere nel formato "@resourceType/resourceName" (ad es. "@style/myCustomStyle") o "0" No
Custom Parcelable app:argType="<type>", dove <type> è il nome completo della classe dell'Parcelable Supporta un valore predefinito di "@null". Non supporta altri valori predefiniti. No
Serializable personalizzato app:argType="<type>", dove <type> è il nome completo della classe dell'Serializable Supporta un valore predefinito di "@null". Non supporta altri valori predefiniti. No
Enum personalizzato app:argType="<type>", dove <type> è il nome completo dell'enum Sì: i valori predefiniti devono corrispondere al nome non qualificato (ad es. "SUCCESS" per corrispondere a MyEnum.SUCCESS). No No

Se un tipo di argomento supporta i valori null, puoi dichiarare un valore predefinito di null utilizzando android:defaultValue="@null".

Le route, i link diretti e gli URI con i relativi argomenti possono essere analizzati dalle stringhe. Ciò non è possibile utilizzando tipi di dati personalizzati come Parcelable e Serializable, come mostrato nella tabella precedente. Per trasmettere dati complessi personalizzati, memorizzali altrove, ad esempio in un ViewModel o in un database, e passa solo un identificatore durante la navigazione. Recupera poi i dati nella nuova posizione al termine della navigazione.

Quando scegli uno dei tipi personalizzati, viene visualizzata la finestra di dialogo Seleziona corso, che ti chiede di scegliere il corso corrispondente per quel tipo. La scheda Progetto consente di scegliere un corso dal progetto corrente.

Puoi scegliere <inferred type> per consentire alla libreria di navigazione di determinare il tipo in base al valore fornito.

Puoi selezionare Array per indicare che l'argomento deve essere un array del valore Tipo selezionato. Nota:

  • Gli array di enum e gli array di riferimenti alle risorse non sono supportati.
  • Gli array supportano i valori nullable, indipendentemente dal supporto dei valori nullable del tipo sottostante. Ad esempio, l'utilizzo di app:argType="integer[]" ti consente di utilizzare app:nullable="true" per indicare che è accettabile passare un array null.
  • Gli array supportano un singolo valore predefinito, "@null". Le matrici non supportano nessun altro valore predefinito.

Sostituire un argomento di destinazione in un'azione

Gli argomenti e i valori predefiniti a livello di destinazione vengono utilizzati da tutte le azioni che indirizzano alla destinazione. Se necessario, puoi sostituire il valore predefinito di un argomento (o impostarne uno se non esiste già) definendo un argomento a livello di azione. Questo argomento deve avere lo stesso nome e lo stesso tipo dell'argomento dichiarato nella destinazione.

Il seguente XML dichiara un'azione con un argomento che sostituisce l'argomento a livello di destinazione dell'esempio precedente:

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

Utilizza Safe Args per trasmettere dati con sicurezza del tipo

Il componente Navigation ha un plug-in Gradle chiamato Safe Args che genera classi di oggetti e builder semplici per la navigazione sicura e l'accesso a eventuali argomenti associati. Safe Args è vivamente consigliato per la navigazione e la trasmissione dei dati, in quanto garantisce la sicurezza del tipo.

Se non utilizzi Gradle, non puoi utilizzare il plug-in SafeArgs. In questi casi, puoi utilizzare i pacchetti per trasmettere direttamente i dati.

Per aggiungere Safe Args al progetto, includi il seguente classpath nel file build.gradle di primo livello:

Groovy

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

Kotlin

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

Devi anche applicare uno dei due plug-in disponibili.

Per generare codice in linguaggio Java adatto per moduli Java o misti Java e Kotlin, aggiungi questa riga al file build.gradle della tua app o del tuo modulo:

Groovy

plugins {
  id 'androidx.navigation.safeargs'
}

Kotlin

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

In alternativa, per generare codice Kotlin adatto per i moduli solo Kotlin, aggiungi:

Groovy

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

Kotlin

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

Devi avere android.useAndroidX=true nel file gradle.properties come da Migrazione ad AndroidX.

Dopo aver attivato Safe Args, il codice generato contiene i seguenti metodi e classi di tipo sicuro per ogni azione, nonché per ogni destinazione di invio e ricezione.

  • Viene creata una classe per ogni destinazione da cui ha origine un'azione. Il nome di questa classe è il nome della destinazione di partenza seguito dalla parola "Indicazioni". Ad esempio, se la destinazione di origine è un frammento denominato SpecifyAmountFragment, la classe generata è chiamata SpecifyAmountFragmentDirections.

    Questa classe ha un metodo per ogni azione definita nella destinazione di origine.

  • Per ogni azione utilizzata per passare l'argomento, viene creata una classe interna il cui nome si basa sull'azione. Ad esempio, se l'azione si chiama confirmationAction,, la classe si chiama ConfirmationAction. Se la tua azione contiene argomenti senza defaultValue, utilizza la classe di azioni associata per impostare il valore degli argomenti.

  • Viene creato un corso per la destinazione di ricezione. Il nome di questa classe è il nome della destinazione a cui è stata aggiunta la parola "Args". Ad esempio, se il frammento di destinazione è denominato ConfirmationFragment,, la classe generata è ConfirmationFragmentArgs. Utilizza il fromBundle() metodo di questa classe per recuperare gli argomenti.

L'esempio seguente mostra come utilizzare questi metodi per impostare un argomento e passarlo al metodo 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);
}

Nel codice per la destinazione di ricezione, utilizza il metodo getArguments() per recuperare il pacchetto e utilizzarne i contenuti. Quando utilizzi le dipendenze -ktx, gli utenti di Kotlin possono anche utilizzare il delegato della proprietà by navArgs() per accedere gli argomenti.

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 + "");
}

Utilizzare Safe Args con un'azione globale

Quando utilizzi Safe Args con un'azione globale, devi fornire un valore android:id per l'elemento <navigation> principale, come mostrato nell'esempio seguente:

<?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 navigazione genera una classe Directions per l'elemento <navigation> basata sul valore android:id. Ad esempio, se hai un elemento <navigation> con android:id=@+id/main_nav, la classe generata si chiama MainNavDirections. Tutte le destinazioni all'interno dell'elemento <navigation> hanno metodi generati per accedere a tutte le azioni globali associate utilizzando gli stessi metodi descritti nella sezione precedente.

Passare dati tra destinazioni con oggetti Bundle

Se non utilizzi Gradle, puoi comunque passare gli argomenti tra le destinazioni utilizzando gli oggetti Bundle. Crea un oggetto Bundle e passalo alla destinazione utilizzando navigate(), come nell'esempio seguente:

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

Nel codice della destinazione di ricezione, utilizza il metodo getArguments() per recuperare il Bundle e utilizzarne i contenuti:

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"));

Passare i dati alla destinazione iniziale

Puoi passare i dati alla destinazione iniziale dell'app. Innanzitutto, devi costruire esplicitamente un Bundle che contenga i dati. Poi, utilizza uno dei seguenti metodi per passare Bundle alla destinazione iniziale:

Per recuperare i dati nella destinazione iniziale, chiama Fragment.getArguments().

Considerazioni su ProGuard

Se riduci il codice, devi impedire che i nomi delle classi Parcelable, Serializable e Enum vengano offuscati durante il processo di minimizzazione. Puoi eseguire questa operazione in due modi:

  • Utilizza le annotazioni @Keep.
  • Utilizza le regole keepnames.

Le sottosezioni seguenti descrivono questi approcci.

Utilizzare le annotazioni @Keep

L'esempio seguente aggiunge annotazioni @Keep alle definizioni delle classi di modelli:

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 { ... }

Utilizzare le regole keepnames

Puoi anche aggiungere regole keepnames al file proguard-rules.pro, come mostrato nell'esempio seguente:

proguard-rules.pro

...

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

...

Risorse aggiuntive

Per scoprire di più sulla navigazione, consulta le seguenti risorse aggiuntive.

Codelab

Video