Nawigacja umożliwia dołączenie danych do operacji nawigacji przez zdefiniowanie argumentów miejsca docelowego. Na przykład miejsce docelowe profilu użytkownika może na podstawie argumentu identyfikatora użytkownika określać, który użytkownik ma zostać wyświetlony.
Ogólnie zalecamy przesyłanie między miejscami docelowymi jak najmniejszej ilości danych. Na przykład należy przekazać klucz, aby pobrać obiekt, a nie sam obiekt, ponieważ w Androidzie łączna ilość miejsca dla wszystkich zapisanych stanów jest ograniczona. Jeśli musisz przekazać duże ilości danych, użyj ViewModel
zgodnie z opisem w opisie ViewModel.
Zdefiniuj argumenty docelowe
Aby przekazać dane między miejscami docelowymi, najpierw zdefiniuj argument, dodając go do miejsca docelowego, które je odbiera. W tym celu wykonaj te czynności:
- W Edytorze nawigacji kliknij miejsce docelowe, które odbiera argument.
- W panelu Atrybuty kliknij Dodaj (+).
- W wyświetlonym oknie Dodaj link do argumentu wpisz nazwę argumentu, typ argumentu, informację, czy argument ma wartość null, oraz w razie potrzeby wartość domyślną.
- Kliknij Dodaj. Zwróć uwagę, że argument pojawi się na liście Argumenty w panelu Atrybuty.
- Następnie kliknij odpowiednie działanie, aby przejść do tego miejsca docelowego. W panelu Atrybuty powinien być widoczny nowo dodany argument w sekcji Wartości domyślne argumentów.
Widać też, że argument został dodany w formacie XML. Kliknij kartę Tekst, aby przełączyć się na widok XML. Zwróć uwagę, że Twój argument został dodany do miejsca docelowego, które go otrzymuje. Oto przykład:
<fragment android:id="@+id/myFragment" > <argument android:name="myArg" app:argType="integer" android:defaultValue="0" /> </fragment>
Obsługiwane typy argumentów
Biblioteka nawigacji obsługuje te typy argumentów:
Typ | Składnia app:argType | Obsługa wartości domyślnych | Obsługiwane wg tras | Z wartością null |
---|---|---|---|---|
Liczba całkowita | app:argType="integer" | Tak | Tak | Nie |
Pływające | app:argType="float" | Tak | Tak | Nie |
Długi | app:argType="long" | Tak – wartości domyślne zawsze muszą kończyć się sufiksem „L” (np. „123L”). | Tak | Nie |
Wartość logiczna | app:argType="boolean" | Tak – „true” lub „false” | Tak | Nie |
Ciąg znaków | app:argType="ciąg znaków" | Tak | Tak | Tak |
Odniesienie do zasobu | app:argType="reference" | Tak – wartości domyślne muszą mieć format „@resourceType/resourceName” (np. „@style/myCustomStyle”) lub „0” | Tak | Nie |
Niestandardowy format Parcelable | app:argType="<type>", gdzie <type> to w pełni kwalifikowana nazwa klasy klasy Parcelable |
Obsługuje wartość domyślną „@null”. Nie obsługuje innych wartości domyślnych. | Nie | Tak |
Niestandardowa możliwa do serializacji | app:argType="<type>", gdzie <type> to w pełni kwalifikowana nazwa klasy klasy Serializable |
Obsługuje wartość domyślną „@null”. Nie obsługuje innych wartości domyślnych. | Nie | Tak |
Niestandardowe wyliczenie | app:argType="<type>", gdzie <type> to pełna i jednoznaczna nazwa wyliczenia | Tak – wartości domyślne muszą pasować do nazwy niekwalifikującej się (np. „SUCCESS” w celu dopasowania do MyEnum.SUCCESS). | Nie | Nie |
Jeśli typ argumentu obsługuje wartości null, możesz zadeklarować domyślną wartość null za pomocą funkcji android:defaultValue="@null"
.
Trasy, precyzyjne linki i identyfikatory URI wraz z ich argumentami można analizować z ciągów tekstowych. Nie można tego zrobić, używając niestandardowych typów danych, takich jak dane Parcelable i Serializable, jak pokazano w poprzedniej tabeli. Aby przekazywać niestandardowe złożone dane, przechowuj je w innym miejscu, np. w modelu ViewModel lub bazie danych, i przekazuj identyfikator tylko podczas nawigacji. Dane możesz pobierać w nowej lokalizacji po zakończeniu nawigacji.
Gdy wybierzesz jeden z typów niestandardowych, pojawi się okno Wybierz klasę z prośbą o wybranie odpowiedniej klasy dla tego typu. Na karcie Projekt możesz wybrać klasę z bieżącego projektu.
Jeśli chcesz, by biblioteka nawigacji określiła typ na podstawie podanej wartości, możesz wybrać <inferred type> (wywnioskowany typ).
Możesz zaznaczyć opcję Tablica, aby wskazać, że argument powinien być tablicą wybranej wartości Typ. Uwaga:
- Tablice wyliczeniowe i tablice odwołań do zasobów nie są obsługiwane.
- Tablice obsługują wartości null niezależnie od obsługi wartości null w podstawowym typie. Na przykład użycie właściwości
app:argType="integer[]"
pozwala użyćapp:nullable="true"
, aby wskazać, że dopuszczalne jest przekazywanie tablicy o wartości null. - Tablice obsługują jedną wartość domyślną – „@null”. Tablice nie obsługują innych wartości domyślnych.
Zastępowanie argumentu docelowego w działaniu
Argumenty na poziomie miejsca docelowego i wartości domyślne są używane we wszystkich działaniach, które prowadzą do tego miejsca docelowego. W razie potrzeby możesz zastąpić domyślną wartość argumentu (lub ustawić ją, jeśli jeszcze nie istnieje), definiując argument na poziomie działania. Ten argument musi mieć taką samą nazwę i typ jak argument zadeklarowany w miejscu docelowym.
Ten kod XML deklaruje działanie za pomocą argumentu, który zastępuje argument na poziomie miejsca docelowego z poprzedniego przykładu:
<action android:id="@+id/startMyFragment"
app:destination="@+id/myFragment">
<argument
android:name="myArg"
app:argType="integer"
android:defaultValue="1" />
</action>
Używaj bezpiecznych argumentów do przekazywania danych z zabezpieczeniami typu
Komponent Nawigacja ma wtyczkę do Gradle o nazwie Safe Args, która generuje proste klasy obiektów i konstruktora, aby zapewnić bezpieczną nawigację i dostęp do wszystkich powiązanych argumentów. Do poruszania się po danych i przekazywania danych zdecydowanie zalecamy korzystanie z bezpiecznych argumentów, ponieważ zapewnia to bezpieczeństwo typów.
Jeśli nie używasz Gradle, nie możesz użyć wtyczki Safe Args. W takich przypadkach możesz używać pakietów do bezpośredniego przekazywania danych.
Aby dodać bezpieczne argumenty,
w swoim projekcie umieść w pliku build.gradle
najwyższego poziomu ten element: classpath
:
Odlotowe
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") } }
Musisz też zastosować jedną z dwóch dostępnych wtyczek.
Aby wygenerować kod w języku Java odpowiedni dla modułów Java lub mieszanych Javy i Kotlin, dodaj
ten wiersz do pliku build.gradle
aplikacji lub modułu:
Odlotowe
plugins { id 'androidx.navigation.safeargs' }
Kotlin
plugins { id("androidx.navigation.safeargs") }
Aby wygenerować kod Kotlin odpowiedni dla modułów wyłącznie Kotlin, dodaj:
Odlotowe
plugins { id 'androidx.navigation.safeargs.kotlin' }
Kotlin
plugins { id("androidx.navigation.safeargs.kotlin") }
Musisz mieć android.useAndroidX=true
w
gradle.properties
plik zgodnie z
Migracja na AndroidaX
Po włączeniu bezpiecznych argumentów wygenerowany kod będzie zawierał następujące bezpieczne klasy i metody dla każdego działania oraz dla każdego miejsca docelowego wysyłania i odbierania.
Klasa jest tworzona dla każdego miejsca docelowego, w którym zainicjowano działanie. Nazwa tej klasy to nazwa początkowego miejsca docelowego z dopiskiem „Trasa”. Jeśli na przykład miejscem docelowym jest fragment o nazwie
SpecifyAmountFragment
, wygenerowana klasa nazywa sięSpecifyAmountFragmentDirections
.Ta klasa ma metodę dla każdego działania zdefiniowanego w źródłowym miejscu docelowym.
Dla każdego działania używanego do przekazywania argumentu tworzona jest klasa wewnętrzna, której nazwa bazuje na działaniu. Jeśli na przykład działanie to
confirmationAction,
, klasa toConfirmationAction
. Jeśli działanie zawiera argumenty bez wartościdefaultValue
, do określania ich wartości używaj powiązanej klasy działania.W miejscu docelowym odbierania tworzona jest klasa. Nazwa tej klasy to nazwa miejsca docelowego ze słowem „Args”. Jeśli np. fragment docelowy to
ConfirmationFragment,
, wygenerowana klasa nazywa sięConfirmationFragmentArgs
. Do pobierania argumentów użyj metodyfromBundle()
tej klasy.
Z przykładu poniżej dowiesz się, jak za pomocą tych metod ustawić argument i przekazać go do metody 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); }
W kodzie miejsca docelowego odbioru użyj metody getArguments()
, aby pobrać pakiet i użyć jego zawartości. Gdy używasz zależności -ktx
, użytkownicy Kotlin mogą też używać delegata właściwości by navArgs()
do uzyskiwania dostępu do argumentów.
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 + ""); }
Używanie bezpiecznych argumentów w działaniu globalnym
Gdy używasz bezpiecznych argumentów z działaniem globalnym, musisz podać wartość android:id
dla głównego elementu <navigation>
, jak pokazano w tym przykładzie:
<?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>
Nawigacja generuje klasę Directions
dla elementu <navigation>
na podstawie wartości android:id
. Jeśli np. masz element <navigation>
z elementem android:id=@+id/main_nav
, wygenerowana klasa nazywa się MainNavDirections
. Wszystkie miejsca docelowe w elemencie <navigation>
wygenerowały metody dostępu do wszystkich powiązanych działań globalnych przy użyciu tych samych metod, które opisaliśmy w poprzedniej sekcji.
Przekazywanie danych między miejscami docelowymi za pomocą obiektów pakietu
Jeśli nie używasz Gradle, nadal możesz przekazywać argumenty między miejscami docelowymi za pomocą obiektów Bundle
. Utwórz obiekt Bundle
i przekaż go do miejsca docelowego za pomocą navigate()
, jak w tym przykładzie:
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);
W kodzie miejsca docelowego odbioru użyj metody getArguments()
, aby pobrać Bundle
i wykorzystać jego zawartość:
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"));
Przekaż dane do miejsca docelowego
Możesz przekazywać dane do początkowego miejsca docelowego aplikacji. Najpierw musisz wyraźnie utworzyć element Bundle
zawierający dane. Następnie użyj jednej z tych metod, aby przekazać Bundle
do miejsca docelowego początkowego:
- Jeśli tworzysz plik
NavHost
automatycznie, wywołajNavHostFragment.create(R.navigation.graph, args)
, gdzieargs
to elementBundle
, który przechowuje Twoje dane. - W przeciwnym razie możesz ustawić argumenty miejsca docelowego początkowego, wywołując jedno z tych przeciążeń typu
NavController.setGraph()
:- Użyj identyfikatora wykresu:
navController.setGraph(R.navigation.graph, args)
- Użyj samego wykresu:
navController.setGraph(navGraph, args)
- Użyj identyfikatora wykresu:
Aby pobrać dane w miejscu docelowym początkowym, wywołaj metodę Fragment.getArguments()
.
Uwagi dotyczące ProGuard
Jeśli zmniejszasz kod, możesz zapobiec zaciemnieniu nazw klas Parcelable
, Serializable
i Enum
w ramach procesu minifikacji. Możesz to zrobić na 2 sposoby:
- Używaj adnotacji @Keep.
- Używaj reguł Keepnames.
Poniżej opisujemy te podejścia.
Używanie adnotacji @Keep
W tym przykładzie dodano adnotacje @Keep
do definicji klas modelu:
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 { ... }
Używanie reguł Keepnames
Do pliku proguard-rules.pro
możesz też dodać reguły keepnames
, jak pokazano w tym przykładzie:
proguard-rules.pro
...
-keepnames class com.path.to.your.ParcelableArg
-keepnames class com.path.to.your.SerializableArg
-keepnames class com.path.to.your.EnumArg
...
Dodatkowe materiały
Więcej informacji o nawigacji znajdziesz w tych dodatkowych materiałach.