Trasmettere dati tra destinazioni

La navigazione consente di collegare dati a un'operazione di navigazione definendo argomenti per una destinazione. Ad esempio, la destinazione di un profilo utente potrebbe richiedere un argomento User-ID per determinare quale utente visualizzare.

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

Definisci gli argomenti di destinazione

Per passare dati tra destinazioni, devi innanzitutto definire 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 può essere associato a un valore null e, se necessario, un valore predefinito.
  4. Fai clic su Aggiungi. Nota che l'argomento ora compare nell'elenco Argomenti nel riquadro Attributi.
  5. Successivamente, fai clic sull'azione corrispondente che ti porta a questa destinazione. Nel riquadro Attributi ora 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 nota che l'argomento è stato aggiunto alla destinazione che riceve l'argomento. Di seguito è riportato un esempio:

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

Tipi di argomenti supportati

La libreria di navigazione supporta i seguenti tipi di argomenti:

Tipo sintassi app:argType Supporto dei valori predefiniti Gestione in base ai percorsi Null
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ì - "vero" o "falso" No
Stringa app:argType="string"
Riferimento alle risorse app:argType="reference" Sì: i valori predefiniti devono essere nel formato "@resourceType/resourceName" (ad es. "@style/myCustomStyle") o "0" No
Personalizzabile in parcelle app:argType="<type>", dove <type> è il nome completo della classe Parcelable Supporta un valore predefinito di "@null". Non supporta altri valori predefiniti. No
Serializzabile personalizzato app:argType="<type>", dove <type> è il nome completo della classe Serializable Supporta un valore predefinito di "@null". Non supporta altri valori predefiniti. No
Enum personalizzata 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 valori nulli, puoi dichiarare un valore predefinito nullo utilizzando android:defaultValue="@null".

Route, link diretti e URI con i relativi argomenti possono essere analizzati dalle stringhe. Ciò non è possibile utilizzando tipi di dati personalizzati come Parcelables e Serializables come mostrato nella tabella precedente. Per trasferire dati complessi personalizzati, memorizza i dati altrove, ad esempio in un ViewModel o in un database, e trasmetti un identificatore solo durante la navigazione; quindi recupera i dati nella nuova località al termine della navigazione.

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

Puoi scegliere <inferred type> per fare in modo che la libreria di navigazione determini il tipo in base al valore fornito.

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

  • Gli array di enum e di riferimenti alle risorse non sono supportati.
  • Gli array supportano valori con valori nulli, indipendentemente dal supporto per i valori con null del tipo sottostante. Ad esempio, l'uso di app:argType="integer[]" ti consente di utilizzare app:nullable="true" per indicare che il passaggio di un array nullo è accettabile.
  • Gli array supportano un singolo valore predefinito, "@null". Gli array non supportano altri valori predefiniti.

Override di un argomento di destinazione in un'azione

Gli argomenti e i valori predefiniti a livello di destinazione sono utilizzati da tutte le azioni che accedino alla destinazione. Se necessario, puoi eseguire l'override del 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 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 Navigazione ha un plug-in Gradle chiamato Safe Args che genera semplici classi di oggetti e builder per una navigazione sicura per tipo e l'accesso a qualsiasi argomento associato. L'utilizzo di Args sicuri è vivamente consigliato per la navigazione e il trasferimento dei dati, perché garantisce la sicurezza dei tipi.

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

Per aggiungere Args sicuri al tuo progetto, includi i seguenti classpath nel file build.gradle di primo livello:

trendy

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

Kotlin

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

Devi anche applicare uno dei due plug-in disponibili.

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

trendy

plugins {
  id 'androidx.navigation.safeargs'
}

Kotlin

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

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

trendy

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

Kotlin

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

Devi avere android.useAndroidX=true nel file gradle.properties, in base alla migrazione ad AndroidX.

Dopo aver abilitato Args sicuri, il codice generato contiene i seguenti metodi e classi di sicurezza del tipo per ogni azione e 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 origine aggiunta alla parola "Indicazioni stradali". Ad esempio, se la destinazione di origine è un frammento denominato SpecifyAmountFragment, la classe generata si chiama 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 è basato sull'azione. Ad esempio, se l'azione si chiama confirmationAction,, la classe si chiama ConfirmationAction. Se l'azione contiene argomenti senza defaultValue, devi utilizzare la classe di azione associata per impostare il valore degli argomenti.

  • Viene creata una classe per la destinazione ricevente. Il nome di questa classe è il nome della destinazione aggiunta alla parola "Args". Ad esempio, se il frammento di destinazione è denominato ConfirmationFragment,, la classe generata si chiama ConfirmationFragmentArgs. Utilizza il metodo fromBundle() 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 della destinazione ricevente, utilizza il metodo getArguments() per recuperare il bundle e utilizzarne i contenuti. Quando si utilizzano le dipendenze -ktx, gli utenti di Kotlin possono utilizzare anche il delegato della proprietà by navArgs() per accedere agli 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 + "");
}

Usa Safe Args in un'azione globale

Quando utilizzi Args sicuri 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> dispongono di metodi generati per accedere a tutte le azioni globali associate utilizzando gli stessi metodi descritti nella sezione precedente.

Trasferire dati tra destinazioni con oggetti Bundle

Se non utilizzi Gradle, puoi comunque passare argomenti tra le destinazioni utilizzando oggetti Bundle. Crea un oggetto Bundle e trasmettilo 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 destinazione, utilizza il metodo getArguments() per recuperare 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"));

Passa i dati alla destinazione di partenza

Puoi passare dati alla destinazione iniziale dell'app. Innanzitutto, devi creare esplicitamente una Bundle che contenga i dati. Quindi, utilizza uno dei seguenti approcci per passare Bundle alla destinazione di partenza:

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

Considerazioni su ProGuard

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

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

Queste sottosezioni descrivono questi approcci.

Utilizzare le annotazioni @Keep

Nell'esempio seguente vengono aggiunte le annotazioni @Keep alle definizioni delle classi dei 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 di 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.

Samples

Codelab

Video