Komponen Navigasi menyediakan bahasa khusus domain berbasis Kotlin, atau
DSL, yang mengandalkan builder yang aman untuk jenis
Kotlin. API ini memungkinkan Anda membuat grafik secara deklaratif dalam kode Kotlin, bukan di dalam resource XML. Hal ini dapat berguna jika Anda ingin membuat navigasi aplikasi
secara dinamis. Misalnya, aplikasi Anda dapat mendownload dan meng-cache
konfigurasi navigasi dari layanan web eksternal, lalu menggunakan
konfigurasi tersebut untuk membuat grafik navigasi secara dinamis dalam fungsi
onCreate() aktivitas Anda.
Dependensi
Untuk menggunakan DSL Kotlin dengan Fragmen, tambahkan dependensi berikut ke file
build.gradle aplikasi Anda:
Groovy
dependencies { def nav_version = "2.10.2" api "androidx.navigation:navigation-fragment-ktx:$nav_version" }
Kotlin
dependencies { val nav_version = "2.10.2" api("androidx.navigation:navigation-fragment-ktx:$nav_version") }
Membuat grafik
Berikut contoh dasar berdasarkan aplikasi Sunflower. Untuk contoh ini, kita memiliki dua tujuan: home dan plant_detail. Tujuan home
ada saat pengguna pertama kali meluncurkan aplikasi. Tujuan ini
menampilkan daftar tanaman dari kebun pengguna. Saat pengguna memilih salah satu
tanaman, aplikasi akan mengarahkan ke tujuan plant_detail.
Gambar 1 menunjukkan tujuan berikut beserta argumen yang diperlukan oleh tujuan plant_detail dan tindakan to_plant_detail yang digunakan aplikasi untuk menavigasi dari home ke plant_detail.
home dan plant_detail, bersama dengan tindakan yang menghubungkan keduanya.Menghosting Grafik Nav DSL Kotlin
Agar dapat membuat grafik navigasi aplikasi, Anda memerlukan tempat untuk menghosting
grafik tersebut. Contoh ini menggunakan fragmen sehingga menghosting grafik dalam
NavHostFragment
di dalam
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>
Perhatikan bahwa atribut app:navGraph tidak ditetapkan dalam contoh ini. Grafik
tidak ditentukan sebagai resource di
folder res/navigation sehingga perlu ditetapkan sebagai bagian dari proses
onCreate() dalam aktivitas.
Dalam XML, sebuah tindakan menggabungkan ID tujuan dengan satu atau beberapa argumen. Namun, saat menggunakan DSL Navigasi, rute dapat berisi argumen sebagai bagian dari rute. Artinya, tidak ada konsep tindakan saat menggunakan DSL.
Langkah berikutnya adalah menentukan rute yang akan Anda gunakan saat menentukan grafik Anda.
Membuat rute untuk grafik
Grafik navigasi berbasis XML diuraikan sebagai bagian
dari proses build Android. Konstanta numerik dibuat untuk setiap atribut id
yang ditentukan dalam grafik. ID statis yang dihasilkan waktu build ini tidak
tersedia saat membuat grafik navigasi saat runtime, sehingga DSL Navigasi
menggunakan jenis
yang dapat diserialisasi, bukan
ID. Setiap rute diwakili oleh jenis yang unik.
Saat menangani argumen, argumen tersebut dibangun ke dalam jenis rute. Dengan begitu, Anda akan memiliki keamanan jenis untuk argumen navigasi.
@Serializable data object Home
@Serializable data class Plant(val id: String)
Membuat grafik dengan NavGraphBuilder DSL
Setelah menentukan rute, Anda dapat membuat grafik navigasi.
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)
}
}
Dalam contoh ini, dua tujuan fragmen ditentukan menggunakan fungsi builder DSL fragment(). Fungsi ini memerlukan dua argumen
jenis
.
Pertama, class Fragment
yang menyediakan UI untuk tujuan ini. Menetapkan ini memiliki efek yang sama seperti
menetapkan atribut android:name pada tujuan fragmen yang ditentukan
menggunakan XML.
Kedua, rute. Jenis ini harus berupa jenis yang dapat diserialisasi yang diperluas dari Any. Class ini
harus berisi argumen navigasi apa pun yang akan digunakan oleh tujuan ini,
dan jenisnya.
Fungsi tersebut juga menerima lambda opsional untuk konfigurasi tambahan, seperti label tujuan, serta fungsi builder tersemat untuk argumen kustom dan deep link.
Menavigasi dengan grafik DSL Kotlin
Terakhir, Anda dapat bernavigasi dari home ke plant_detail menggunakan panggilan
NavController.navigate():
private fun navigateToPlant(plantId: String) {
findNavController().navigate(route = PlantDetail(id = plantId))
}
Di PlantDetailFragment, Anda bisa mendapatkan argumen navigasi dengan mendapatkan
NavBackStackEntry
saat ini
dan memanggil
toRoute
di dalamnya untuk mendapatkan instance rute.
val plantDetailRoute = findNavController().getBackStackEntry<PlantDetail>().toRoute<PlantDetail>()
val plantId = plantDetailRoute.id
Jika PlantDetailFragment menggunakan ViewModel, dapatkan instance rute menggunakan
SavedStateHandle.toRoute.
val plantDetailRoute = savedStateHandle.toRoute<PlantDetail>()
val plantId = plantDetailRoute.id
Bagian lainnya dalam panduan ini menjelaskan elemen grafik navigasi umum, tujuan, dan cara menggunakannya saat membuat grafik.
Tujuan
DSL Kotlin menyediakan dukungan bawaan untuk tiga jenis tujuan:
tujuan Fragment, Activity, dan NavGraph, yang masing-masing memiliki
fungsi ekstensi inline yang tersedia untuk mem-build dan mengonfigurasi
tujuan.
Tujuan fragmen
Fungsi DSL
fragment()
dapat diparameterisasi dengan class fragmen untuk UI dan
jenis rute yang digunakan untuk mengidentifikasi tujuan ini secara unik, diikuti dengan lambda
tempat Anda dapat memberikan konfigurasi tambahan seperti yang dijelaskan di bagian Menavigasi
dengan grafik DSL Kotlin.
fragment<MyFragment, MyRoute> {
label = getString(R.string.fragment_title)
// custom argument types, deepLinks
}
Tujuan aktivitas
Fungsi DSL
activity()
mengambil parameter jenis untuk rute, tetapi tidak diparameterisasi ke
class aktivitas penerapan apa pun. Sebagai gantinya, Anda menetapkan activityClass opsional di
lambda akhir. Fleksibilitas ini memungkinkan Anda menentukan tujuan aktivitas untuk
aktivitas yang harus diluncurkan menggunakan intent
implisit, jika class
aktivitas eksplisit tidak memungkinkan. Seperti tujuan fragmen, Anda juga dapat
mengonfigurasi label, argumen kustom, dan deep link.
activity<MyRoute> {
label = getString(R.string.activity_title)
// custom argument types, deepLinks...
activityClass = MyActivity::class
}
Tujuan grafik navigasi
Fungsi DSL
navigation()
dapat digunakan untuk membuat grafik navigasi
bertingkat. Fungsi ini memerlukan parameter jenis
untuk rute yang akan ditetapkan ke grafik ini. Fungsi ini juga memerlukan dua argumen:
rute tujuan awal grafik, dan lambda untuk mengonfigurasi
grafik lebih lanjut. Elemen yang valid mencakup tujuan, jenis argumen kustom, deep link, dan label deskriptif untuk tujuan.
Label ini dapat berguna untuk mengikat grafik navigasi ke komponen UI menggunakan
NavigationUI.
@Serializable data object HomeGraph
@Serializable data object Home
navigation<HomeGraph>(startDestination = Home) {
// label, other destinations, deep links
}
Mendukung tujuan kustom
Jika Anda menggunakan jenis tujuan baru
yang tidak langsung mendukung DSL Kotlin, Anda dapat menambahkan tujuan ini ke
DSL Kotlin menggunakan
addDestination():
// The NavigatorProvider is retrieved from the NavController
val customDestination = navigatorProvider[CustomNavigator::class].createDestination().apply {
route = Graph.CustomDestination.route
}
addDestination(customDestination)
Sebagai alternatif, Anda juga dapat menggunakan operator unary plus untuk menambahkan tujuan yang baru dibuat langsung ke grafik:
// The NavigatorProvider is retrieved from the NavController
+navigatorProvider[CustomNavigator::class].createDestination().apply {
route = Graph.CustomDestination.route
}
Menyediakan argumen tujuan
Argumen tujuan dapat ditentukan sebagai bagian dari class rute. Ini dapat ditentukan dengan cara yang sama seperti yang Anda lakukan untuk class Kotlin apa pun. Argumen yang diperlukan ditentukan sebagai jenis non-nullable dan argumen opsional ditentukan dengan nilai default.
Mekanisme dasar untuk merepresentasikan rute dan argumennya berbasis
string. Penggunaan string untuk membuat model rute memungkinkan status navigasi disimpan dan
dipulihkan dari disk selama perubahan
konfigurasi dan penghentian proses yang dimulai
sistem. Oleh karena itu,
setiap argumen navigasi harus dapat diserialisasi, yaitu harus memiliki
metode yang mengonversi representasi dalam memori dari nilai argumen menjadi
String.
Plugin serialisasi Kotlin otomatis menghasilkan metode serialisasi untuk jenis dasar saat anotasi @Serializable ditambahkan ke objek.
@Serializable
data class MyRoute(
val id: String,
val myList: List<Int>,
val optionalArg: String? = null
)
fragment<MyFragment, MyRoute>
Menyediakan jenis kustom
Untuk jenis argumen kustom, Anda harus menyediakan class NavType kustom. Hal ini
memungkinkan Anda mengontrol dengan tepat cara jenis Anda diuraikan dari rute atau deep link.
Misalnya, rute yang digunakan untuk menentukan layar penelusuran dapat berisi class yang mewakili parameter penelusuran:
@Serializable
data class SearchRoute(val parameters: SearchParameters)
@Serializable
@Parcelize
data class SearchParameters(
val searchQuery: String,
val filters: List<String>
)
NavType kustom dapat ditulis sebagai:
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)
}
}
Jenis ini kemudian dapat digunakan di DSL Kotlin Anda seperti jenis lainnya:
fragment<SearchFragment, SearchRoute>(
typeMap = mapOf(typeOf<SearchParameters>() to SearchParametersType)
) {
label = getString(R.string.plant_search_title)
}
Saat membuka tujuan, buat instance rute Anda:
val params = SearchParameters("rose", listOf("available"))
navController.navigate(route = SearchRoute(params))
Parameter ini dapat diperoleh dari rute dalam tujuan:
val searchRoute = navController().getBackStackEntry<SearchRoute>().toRoute<SearchRoute>()
val params = searchRoute.parameters
Deep link
Deep link dapat ditambahkan ke tujuan mana pun, seperti yang dapat dilakukan dengan grafik navigasi berbasis XML. Semua prosedur yang sama seperti yang ditentukan dalam Membuat deep link untuk tujuan berlaku untuk proses pembuatan deep link menggunakan DSL Kotlin.
Namun, saat membuat deep link implisit,
Anda tidak memiliki resource navigasi XML yang dapat dianalisis untuk
elemen <deepLink>. Oleh karena itu, Anda tidak dapat mengandalkan penempatan elemen <nav-graph>
di file AndroidManifest.xml dan harus menambahkan filter
intent ke aktivitas Anda secara manual. Filter intent yang Anda berikan harus cocok dengan jalur dasar, tindakan, dan mimetype deep link aplikasi Anda.
Deep link ditambahkan ke tujuan dengan memanggil fungsi deepLink di dalam
lambda tujuan. Fungsi ini menerima rute sebagai jenis berparameter, dan
parameter basePath untuk jalur dasar URL yang digunakan untuk deep link.
Anda juga dapat menambahkan tindakan dan mimetype menggunakan lambda di akhir
deepLinkBuilder.
Contoh berikut membuat URI deep link untuk tujuan 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 URI
Format URI deep link otomatis dibuat dari kolom rute menggunakan aturan berikut:
- Parameter yang diperlukan ditambahkan sebagai parameter jalur (contoh:
/{id}) - Parameter dengan nilai default (parameter opsional) ditambahkan sebagai parameter kueri (contoh:
?name={name}) - Koleksi ditambahkan sebagai parameter kueri (contoh:
?items={value1}&items={value2}) - Urutan parameter sesuai dengan urutan kolom dalam rute
Misalnya, jenis rute berikut:
@Serializable data class PlantDetail(
val id: String,
val name: String,
val colors: List<String>,
val latinName: String? = null,
)
memiliki format URI yang dihasilkan:
basePath/{id}/{name}/?colors={color1}&colors={color2}&latinName={latinName}
Tidak ada batasan jumlah deep link yang dapat Anda tambahkan. Setiap kali Anda memanggil
deepLink(),
deep link baru akan ditambahkan ke daftar yang dikelola untuk tujuan tersebut.
Batasan
Plugin Safe Args tidak
kompatibel dengan DSL Kotlin, karena plugin tersebut mencari file resource XML untuk
menghasilkan class Directions dan Arguments.