Komponent Nawigacja udostępnia język oparty na domenie Kotlin.
która bazuje na technologii Kotlin bezpiecznej dla typu
budownicze
, Ten interfejs API umożliwia deklaratywnie tworzenie grafu w kodzie Kotlina, a nie w zasobach XML. Może to być przydatne, jeśli chcesz tworzyć
porusza się dynamicznie. Aplikacja może na przykład pobierać i przechowywać w pamięci podręcznej konfigurację nawigacji z zewnętrznej usługi internetowej, a następnie używać tej konfiguracji do dynamicznego tworzenia grafu nawigacji w funkcji onCreate() aktywności.
Zależności
Aby używać DSL Kotlin z fragmentami, dodaj następującą zależność do funkcji
Plik build.gradle:
Groovy
dependencies { def nav_version = "2.9.5" api "androidx.navigation:navigation-fragment-ktx:$nav_version" }
Kotlin
dependencies { val nav_version = "2.9.5" api("androidx.navigation:navigation-fragment-ktx:$nav_version") }
Tworzenie wykresu
Oto podstawowy przykład oparty na aplikacji Sunflower. W tym przykładzie mamy 2 miejsca docelowe: home i plant_detail. home
miejsce docelowe jest obecne przy pierwszym uruchomieniu aplikacji przez użytkownika. To miejsce docelowe
wyświetla listę roślin z ogrodu użytkownika. Gdy użytkownik wybierze jedną z tych opcji:
rośliny, aplikacja przejdzie do miejsca docelowego plant_detail.
Rys. 1 przedstawia te miejsca docelowe wraz z argumentami wymaganymi przez
miejsce docelowe plant_detail i działanie to_plant_detail używane przez aplikację
aby przejść z home do plant_detail.
home i plant_detail, a także działanie, które je łączy.Hostowanie grafu nawigacyjnego Kotlin DSL
Aby utworzyć wykres nawigacyjny aplikacji, musisz mieć miejsce do przechowywania
wykres. W tym przykładzie użyto fragmentów, dlatego wykres jest przechowywany w
NavHostFragment
wewnątrz
FragmentContainerView:
<!-- activity_garden.xml -->
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/nav_host"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true" />
</FrameLayout>
Zwróć uwagę, że w tym przykładzie atrybut app:navGraph nie jest ustawiony. Wykres
nie jest zdefiniowany jako zasób w
folderu res/navigation, więc należy go ustawić jako część folderu onCreate()
w trakcie aktywności.
W kodzie XML działanie łączy identyfikator miejsca docelowego z co najmniej 1 argumentem. Jednak podczas korzystania z DSL nawigacji trasa może zawierać argumenty jako część trasy. Oznacza to, że przy korzystaniu z DSL nie ma koncepcji działań.
Następnym krokiem jest zdefiniowanie tras, których będziesz używać do definiowania wykres.
Tworzenie tras na wykresie
Grafiki nawigacji w formacie XML są analizowane w ramach procesu kompilacji Androida. Dla każdego elementu id tworzona jest stała liczbowa.
zdefiniowany na wykresie. Te identyfikatory statyczne generowane w czasie kompilacji nie są dostępne podczas tworzenia grafu nawigacji w czasie wykonywania, dlatego w jego przypadku język programowania DSL do nawigacji używa typów serializowanych zamiast identyfikatorów. Każda trasa ma swój unikalny typ.
Gdy rozmawiamy z argumentami, są one wbudowane w proces . Dzięki temu masz pewność, że argumenty nawigacji są bezpieczne.
@Serializable data object Home
@Serializable data class Plant(val id: String)
Tworzenie grafu za pomocą DSL NavGraphBuilder
Po zdefiniowaniu tras możesz utworzyć graf nawigacji.
val navController = findNavController(R.id.nav_host_fragment)
navController.graph = navController.createGraph(
startDestination = Home
) {
fragment<HomeFragment, Home> {
label = resources.getString(R.string.home_title)
}
fragment<PlantDetailFragment, PlantDetail> {
label = resources.getString(R.string.plant_detail_title)
}
}
W tym przykładzie 2 miejsca docelowe fragmentów są zdefiniowane za pomocą atrybutu
fragment()
Funkcja konstruktora DSL. Ta funkcja wymaga dwóch typów
,
Najpierw klasa Fragment, która zapewnia interfejs użytkownika dla tego miejsca docelowego. Ustawienie tej opcji ma taki sam skutek jak ustawienie atrybutu android:name w miejscach docelowych fragmentów zdefiniowanych za pomocą kodu XML.
Po drugie, trasa. Musi to być serializowalny typ, który rozciąga się od Any. it
powinien zawierać wszelkie argumenty nawigacyjne używane przez to miejsce docelowe,
i ich rodzajach.
Funkcja ta akceptuje też opcjonalną funkcję Lambda do dodatkowej konfiguracji, np. etykiety docelowej, a także osadzone funkcje kreatora do niestandardowych argumentów i linków bezpośrednich.
Poruszanie się po diagramie DSL w Kotlinie
Na koniec możesz przejść z home do plant_detail za pomocą NavController.navigate():
private fun navigateToPlant(plantId: String) {
findNavController().navigate(route = PlantDetail(id = plantId))
}
W PlantDetailFragment możesz uzyskać argumenty nawigacji, uzyskując bieżący obiekt NavBackStackEntry i wywołując na nim metodę toRoute, aby uzyskać instancję trasy.
val plantDetailRoute = findNavController().getBackStackEntry<PlantDetail>().toRoute<PlantDetail>()
val plantId = plantDetailRoute.id
Jeśli aplikacja PlantDetailFragment używa modułu ViewModel, pobierz instancję trasy za pomocą funkcji SavedStateHandle.toRoute.
val plantDetailRoute = savedStateHandle.toRoute<PlantDetail>()
val plantId = plantDetailRoute.id
W dalszej części tego przewodnika opisujemy typowe elementy grafu nawigacyjnego, czyli miejsca docelowe, oraz sposób ich wykorzystania podczas tworzenia grafu.
Miejsca docelowe
Kotlin DSL ma wbudowaną obsługę trzech typów miejsc docelowych:
Fragment, Activity i NavGraph miejsca docelowe, z których każde ma własne
wbudowana funkcja rozszerzenia dostępna do tworzenia i konfigurowania
miejsce docelowe.
Miejsca docelowe fragmentów kodu
fragment()
Funkcję DSL można zdefiniować za pomocą klasy fragmentu dla interfejsu użytkownika i funkcji
typ trasy użyty do jednoznacznego wskazania miejsca docelowego z dodanym parametrem lambda
gdzie można wprowadzić dodatkową konfigurację, zgodnie z opisem w sekcji Nawigacja
z wykresem Kotlin DSL.
fragment<MyFragment, MyRoute> {
label = getString(R.string.fragment_title)
// custom argument types, deepLinks
}
Miejsce docelowe aktywności
Funkcja DSL activity() przyjmuje parametr typu dla trasy, ale nie jest parametryzowana pod kątem żadnej klasy implementującej aktywność. Zamiast tego możesz ustawić opcjonalny activityClass w
lambda na końcu. Dzięki tej elastyczności możesz zdefiniować miejsce docelowe aktywności
działania, które należy rozpocząć za pomocą domyślnego
intencja, gdzie jawny
nie miałyby sensu. Podobnie jak w przypadku miejsc docelowych fragmentów, możesz też
skonfigurować etykietę,
niestandardowe argumenty i precyzyjne linki.
activity<MyRoute> {
label = getString(R.string.activity_title)
// custom argument types, deepLinks...
activityClass = MyActivity::class
}
Miejsce docelowe wykresu nawigacyjnego
navigation()
Za pomocą funkcji DSL można utworzyć zagnieżdżoną nawigację
wykres. Ta funkcja przyjmuje typ
trasy, którą należy przypisać do tego wykresu. Przyjmuje też 2 argumenty:
trasę początkowego miejsca docelowego wykresu, a parametr lambda pozwoli kontynuować
skonfigurować wykres. Dozwolone elementy to inne miejsca docelowe, niestandardowe typy argumentów, precyzyjne linki i etykieta opisowa miejsca docelowego.
Ta etykieta może być przydatna do powiązania grafu nawigacji z komponentami interfejsu użytkownika za pomocą NavigationUI.
@Serializable data object HomeGraph
@Serializable data object Home
navigation<HomeGraph>(startDestination = Home) {
// label, other destinations, deep links
}
Obsługa niestandardowych miejsc docelowych
Jeśli używasz nowego typu miejsca docelowego, które nie obsługuje bezpośrednio Kotlin DSL, możesz dodać te miejsca docelowe do Kotlin DSL za pomocą addDestination():
// The NavigatorProvider is retrieved from the NavController
val customDestination = navigatorProvider[CustomNavigator::class].createDestination().apply {
route = Graph.CustomDestination.route
}
addDestination(customDestination)
Możesz też użyć operatora jednoargumentowego plusa, aby dodać nowo utworzone miejsce docelowe bezpośrednio do grafu:
// The NavigatorProvider is retrieved from the NavController
+navigatorProvider[CustomNavigator::class].createDestination().apply {
route = Graph.CustomDestination.route
}
Podawanie argumentów miejsca docelowego
Argumenty miejsca docelowego można zdefiniować jako część klasy trasy. Można je zdefiniować w taki sam sposób jak dowolną klasę Kotlina. Wymagane argumenty są zdefiniowane jako typy, które nie mogą być puste, a opcjonalne argumenty są zdefiniowane z wartościami domyślnymi.
Mechanizm reprezentowania tras i ich argumentów opiera się na ciągach znaków. Używanie ciągów tekstowych do modelowania tras umożliwia przechowywanie stanu nawigacji
przywrócone z dysku podczas konfiguracji
zmian i procesu inicjowanego przez system
śmierć. Z tego powodu każdy argument nawigacji musi być serializowany, czyli musi mieć metodę, która konwertuje reprezentację wartości argumentu w pamięci na String.
Wtyczka do serializacji w Kotlinie automatycznie generuje metody serializacji dla podstawowych typów, gdy do obiektu zostanie dodana adnotacja @Serializable.
@Serializable
data class MyRoute(
val id: String,
val myList: List<Int>,
val optionalArg: String? = null
)
fragment<MyFragment, MyRoute>
Podawanie typów niestandardowych
W przypadku niestandardowych typów argumentów musisz podać niestandardową klasę NavType. Dzięki temu możesz dokładnie określić, jak Twój typ ma być analizowany z trasy lub precyzyjnego linku.
Na przykład trasa używana do zdefiniowania ekranu wyszukiwania może zawierać klasę, która reprezentuje parametry wyszukiwania:
@Serializable
data class SearchRoute(val parameters: SearchParameters)
@Serializable
@Parcelize
data class SearchParameters(
val searchQuery: String,
val filters: List<String>
)
Niestandardowa wartość NavType może mieć postać:
val SearchParametersType = object : NavType<SearchParameters>(
isNullableAllowed = false
) {
override fun put(bundle: Bundle, key: String, value: SearchParameters) {
bundle.putParcelable(key, value)
}
override fun get(bundle: Bundle, key: String): SearchParameters {
return bundle.getParcelable(key) as SearchParameters
}
override fun serializeAsValue(value: SearchParameters): String {
// Serialized values must always be Uri encoded
return Uri.encode(Json.encodeToString(value))
}
override fun parseValue(value: String): SearchParameters {
// Navigation takes care of decoding the string
// before passing it to parseValue()
return Json.decodeFromString<SearchParameters>(value)
}
}
Możesz go potem używać w Kotlin DSL tak jak każdego innego typu:
fragment<SearchFragment, SearchRoute>(
typeMap = mapOf(typeOf<SearchParameters>() to SearchParametersType)
) {
label = getString(R.string.plant_search_title)
}
Podczas nawigacji do miejsca docelowego utwórz instancję trasy:
val params = SearchParameters("rose", listOf("available"))
navController.navigate(route = SearchRoute(params))
Parametr można uzyskać z trasy w miejscu docelowym:
val searchRoute = navController().getBackStackEntry<SearchRoute>().toRoute<SearchRoute>()
val params = searchRoute.parameters
Precyzyjne linki
Precyzyjne linki można dodawać do dowolnego miejsca docelowego, tak jak w przypadku XML-owego grafu nawigacyjnego. Do procesu tworzenia precyzyjnego linku za pomocą Kotlin DSL mają zastosowanie wszystkie procedury zdefiniowane w sekcji Tworzenie precyzyjnego linku do miejsca docelowego.
Podczas tworzenia domyślnego precyzyjnego linku nie masz jednak zasobu nawigacji XML, który można by przeanalizować pod kątem elementów <deepLink>. Dlatego nie można polegać na umieszczeniu <nav-graph>
element w pliku AndroidManifest.xml i musi zamiast tego dodać atrybut intent
ręcznie dodawać filtry do aktywności. Podany przez Ciebie filtr intencji powinien pasować do ścieżki podstawowej, działania i typu MIME precyzyjnych linków w aplikacji.
Precyzyjne linki są dodawane do miejsca docelowego przez wywołanie w nim funkcji deepLink
lambda dla miejsca docelowego. Akceptuje on trasę jako typ z parametrami,
basePath jako ścieżki podstawowej adresu URL używanego na potrzeby precyzyjnego linku.
Możesz również dodać działanie i typ MIME za pomocą funkcji
deepLinkBuilder
lambda na końcu.
W tym przykładzie tworzymy identyfikator URI precyzyjnego linku do miejsca docelowego Home.
@Serializable data object Home
fragment<HomeFragment, Home>{
deepLink<Home>(basePath = "www.example.com/home"){
// Optionally, specify the action and/or mime type that this destination
// supports
action = "android.intent.action.MY_ACTION"
mimeType = "image/*"
}
}
Format identyfikatora URI
Format identyfikatora URI precyzyjnego linku jest generowany automatycznie na podstawie pól trasy przy użyciu tych reguł:
- Wymagane parametry są dodawane jako parametry ścieżki (przykład:
/{id}). - Parametry z wartością domyślną (parametry opcjonalne) są dołączane jako parametry zapytania (np.
?name={name}). - Kolekcje są dodawane jako parametry zapytania (przykład:
?items={value1}&items={value2}). - Kolejność parametrów odpowiada kolejności pól na trasie
Na przykład taki typ trasy:
@Serializable data class PlantDetail(
val id: String,
val name: String,
val colors: List<String>,
val latinName: String? = null,
)
ma wygenerowany format URI:
basePath/{id}/{name}/?colors={color1}&colors={color2}&latinName={latinName}
Nie ma limitu liczby precyzyjnych linków, które możesz dodać. Za każdym razem, gdy do kogoś dzwonisz
deepLink()
nowy precyzyjny link zostanie dołączony do listy prowadzonej w przypadku tego miejsca docelowego.
Ograniczenia
Wtyczka Safe Args
niekompatybilny z DSL Kotlin, ponieważ wtyczka szuka plików zasobów XML w celu
do wygenerowania klas Directions i Arguments.