คอมโพเนนต์การนำทางมีภาษาเฉพาะโดเมนตาม Kotlin หรือ
DSL ที่ใช้ประเภท type-safe ของ Kotlin
เครื่องมือสร้าง
ที่ใช้เวลาเพียง 2 นาที API นี้ช่วยให้คุณเขียนกราฟในโค้ด Kotlin ได้อย่างชัดเจนแทน
มากกว่าภายในทรัพยากร XML ซึ่งจะเป็นประโยชน์หากคุณต้องการสร้างแอป
การนำทางแบบไดนามิก เช่น แอปอาจดาวน์โหลดและแคช
การกำหนดค่าการนำทางจากบริการเว็บภายนอกแล้วใช้
เพื่อสร้างกราฟการนำทางแบบไดนามิกใน
onCreate()
การขึ้นต่อกัน
หากต้องการใช้ Kotlin DSL กับ Fragments ให้เพิ่มทรัพยากร Dependency ต่อไปนี้ในส่วน
build.gradle
ไฟล์:
Groovy
dependencies { def nav_version = "2.8.4" api "androidx.navigation:navigation-fragment-ktx:$nav_version" }
Kotlin
dependencies { val nav_version = "2.8.4" api("androidx.navigation:navigation-fragment-ktx:$nav_version") }
การสร้างกราฟ
ต่อไปนี้เป็นตัวอย่างพื้นฐานที่อ้างอิงจากดอกทานตะวัน
แอป สำหรับกรณีนี้
ตัวอย่างเช่น เรามีปลายทาง 2 แห่ง ได้แก่ home
และ plant_detail
home
ปลายทาง จะแสดงเมื่อผู้ใช้เปิดแอปเป็นครั้งแรก จุดหมายนี้
แสดงรายการต้นไม้จากสวนของผู้ใช้ เมื่อผู้ใช้เลือกหนึ่งใน
ต้นไม้ แอปจะนำทางไปยังจุดหมาย plant_detail
รูปที่ 1 แสดงปลายทางเหล่านี้พร้อมกับอาร์กิวเมนต์ที่แอตทริบิวต์
ปลายทาง plant_detail
และการดำเนินการ to_plant_detail
ที่แอปใช้
เพื่อนำทางจาก home
ไป plant_detail
โฮสติ้ง Kotlin DSL Nav Graph
ก่อนที่คุณจะสร้างกราฟการนำทางของแอปได้ คุณต้องมีสถานที่เพื่อโฮสต์
กราฟ ตัวอย่างนี้ใช้ Fragment จึงโฮสต์กราฟใน
NavHostFragment
ภายใน
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>
โปรดสังเกตว่าไม่มีการตั้งค่าแอตทริบิวต์ app:navGraph
ในตัวอย่างนี้ กราฟ
ไม่ได้หมายถึงเป็นทรัพยากรใน
โฟลเดอร์ res/navigation
จึงต้องมีการตั้งค่าให้เป็นส่วนหนึ่งของ onCreate()
ในกิจกรรม
ใน XML การทำงานจะเชื่อมโยงรหัสปลายทางเข้ากับอาร์กิวเมนต์อย่างน้อย 1 รายการ แต่เมื่อใช้ DSL การนำทาง เส้นทางอาจมีอาร์กิวเมนต์เป็นส่วนหนึ่งของ เส้นทาง ซึ่งหมายความว่าจะไม่มีแนวคิดเกี่ยวกับการดำเนินการเมื่อใช้ DSL
ขั้นตอนถัดไปคือการกำหนดเส้นทางที่คุณจะใช้ในการกำหนด กราฟ
สร้างเส้นทางสำหรับกราฟของคุณ
กราฟการนำทางแบบ XML ถูกแยกวิเคราะห์เป็นส่วนหนึ่งของ
ของกระบวนการบิลด์ของ Android ระบบจะสร้างค่าคงที่ตัวเลขสำหรับ id
แต่ละรายการ
ที่กำหนดไว้ในกราฟ รหัสแบบคงที่ที่สร้างขึ้นในเวลาบิลด์เหล่านี้ไม่ได้
พร้อมใช้งานเมื่อสร้างกราฟการนำทางขณะรันไทม์ เพื่อให้ DSL การนำทาง
ใช้ ซีเรียลได้
ประเภท แทนที่จะเป็น
รหัส แต่ละเส้นทางจะแสดงด้วยประเภทที่ไม่ซ้ำกัน
เมื่อจัดการกับอาร์กิวเมนต์ อาร์กิวเมนต์เหล่านี้จะสร้างขึ้นในเส้นทาง type ซึ่งจะช่วยให้คุณพิมพ์คำว่า "ปลอดภัย" สำหรับอาร์กิวเมนต์การนำทาง
@Serializable data object Home
@Serializable data class Plant(val id: String)
สร้างกราฟด้วย DSL NavGraphBuilder
เมื่อกำหนดเส้นทางแล้ว คุณสามารถสร้างกราฟการนำทางได้
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)
}
}
ในตัวอย่างนี้ เราได้กำหนดปลายทาง Fragment 2 รายการโดยใช้
fragment()
ฟังก์ชันเครื่องมือสร้าง DSL ฟังก์ชันนี้ต้องใช้ประเภท
อาร์กิวเมนต์
ที่ใช้เวลาเพียง 2 นาที
ก่อนอื่น ชั้นเรียน Fragment
ที่สร้าง UI สำหรับปลายทางนี้ การตั้งค่านี้ให้ผลเหมือนกับ
การตั้งค่าแอตทริบิวต์ android:name
ในปลายทางส่วนย่อยที่กำหนดไว้
โดยใช้ XML
อย่างที่ 2 คือเส้นทาง ต้องเป็นประเภทที่ต่อเนื่องได้ซึ่งเริ่มจาก Any
ทั้งนี้
ควรมีอาร์กิวเมนต์การนำทางที่ปลายทางนี้จะใช้
และประเภทของพวกเขา
ฟังก์ชันนี้ยังยอมรับ lambda ที่ไม่บังคับสำหรับการกำหนดค่าเพิ่มเติมด้วย เช่น เป็นป้ายกำกับปลายทาง รวมทั้งฟังก์ชันของเครื่องมือสร้างแบบฝังสำหรับ อาร์กิวเมนต์และ Deep Link
การนำทางด้วยกราฟ DSL ของ Kotlin
สุดท้าย คุณสามารถนำทางจาก home
ไปยัง plant_detail
โดยใช้
NavController.navigate()
การโทร:
private fun navigateToPlant(plantId: String) {
findNavController().navigate(route = PlantDetail(id = plantId))
}
ใน PlantDetailFragment
คุณสามารถดูอาร์กิวเมนต์การนำทางโดยการเรียก
ปัจจุบัน
NavBackStackEntry
และการโทร
toRoute
ในไฟล์ดังกล่าวเพื่อรับอินสแตนซ์ของเส้นทาง
val plantDetailRoute = findNavController().getBackStackEntry<PlantDetail>().toRoute<PlantDetail>()
val plantId = plantDetailRoute.id
หาก PlantDetailFragment
ใช้ ViewModel
อยู่ ให้รับอินสแตนซ์เส้นทางโดยใช้
SavedStateHandle.toRoute
val plantDetailRoute = savedStateHandle.toRoute<PlantDetail>()
val plantId = plantDetailRoute.id
ส่วนที่เหลือของคู่มือนี้จะอธิบายองค์ประกอบทั่วไปของกราฟการนำทาง ปลายทาง และวิธีใช้เมื่อสร้างกราฟ
จุดหมาย
Kotlin DSL รองรับปลายทาง 3 ประเภทในตัว ดังนี้
ปลายทาง Fragment
, Activity
และ NavGraph
รายการมีปลายทางของตัวเอง
ฟังก์ชันส่วนขยายแบบอินไลน์ที่สร้างและกำหนดค่า
ปลายทาง
ปลายทางของ Fragment
fragment()
ฟังก์ชัน DSL สามารถทำเป็นพารามิเตอร์ได้ด้วยคลาส Fragment สำหรับ UI และ
ประเภทเส้นทางที่ใช้ระบุจุดหมายนี้โดยไม่ซ้ำกัน ตามด้วยแลมบ์ดา
ซึ่งคุณสามารถกำหนดค่าเพิ่มเติมตามที่อธิบายไว้ในการนำทาง
ด้วยส่วนกราฟ Kotlin DSL
fragment<MyFragment, MyRoute> {
label = getString(R.string.fragment_title)
// custom argument types, deepLinks
}
จุดหมายของกิจกรรม
activity()
ฟังก์ชัน DSL ใช้พารามิเตอร์ประเภทสำหรับเส้นทาง แต่ไม่ได้แปลงเป็นพารามิเตอร์
ประเภทกิจกรรมที่ติดตั้งใช้งาน แต่คุณตั้งค่า activityClass
แบบไม่บังคับใน
แลมบ์ดาที่ตามหลังอยู่ ความยืดหยุ่นนี้ช่วยให้คุณสามารถกำหนดจุดหมายของกิจกรรมสำหรับ
กิจกรรมที่ควรเริ่มโดยโดยนัย
Intent โดยลิงก์
ก็จะไม่สมเหตุสมผล เช่นเดียวกับปลายทางของส่วนย่อย คุณยังสามารถ
กำหนดค่าป้ายกำกับ อาร์กิวเมนต์ที่กำหนดเอง และ Deep Link
activity<MyRoute> {
label = getString(R.string.activity_title)
// custom argument types, deepLinks...
activityClass = MyActivity::class
}
ปลายทางของกราฟการนำทาง
navigation()
สามารถใช้ฟังก์ชัน DSL เพื่อสร้างการนำทางที่ซ้อนกัน
กราฟ ฟังก์ชันนี้ใช้ประเภท
สำหรับเส้นทางที่กำหนดให้กับกราฟนี้ และมีอาร์กิวเมนต์ 2 แบบ ดังนี้
เส้นทางไปยังจุดหมายเริ่มต้นของกราฟ และ lambda เพื่อไปให้ไกลขึ้น
กำหนดค่ากราฟ องค์ประกอบที่ถูกต้องรวมถึงปลายทางอื่นๆ, อาร์กิวเมนต์ที่กำหนดเอง
ประเภท ลิงก์ในรายละเอียด และป้ายกำกับที่อธิบายสำหรับ
ปลายทาง
ป้ายกำกับนี้อาจเป็นประโยชน์ในการเชื่อมโยงกราฟการนำทางกับคอมโพเนนต์ UI โดยใช้
NavigationUI
@Serializable data object HomeGraph
@Serializable data object Home
navigation<HomeGraph>(startDestination = Home) {
// label, other destinations, deep links
}
การรองรับปลายทางที่กำหนดเอง
หากคุณใช้ประเภทปลายทางใหม่
ที่ไม่รองรับ Kotlin DSL โดยตรง คุณจะเพิ่มปลายทางเหล่านี้ได้
Kotlin DSL โดยใช้
addDestination()
// The NavigatorProvider is retrieved from the NavController
val customDestination = navigatorProvider[CustomNavigator::class].createDestination().apply {
route = Graph.CustomDestination.route
}
addDestination(customDestination)
หรืออีกวิธีหนึ่ง คุณสามารถใช้โอเปอเรเตอร์เครื่องหมายบวก (Unary Plus) เพื่อเพิ่ม ปลายทางที่สร้างขึ้นโดยตรงลงในกราฟ
// The NavigatorProvider is retrieved from the NavController
+navigatorProvider[CustomNavigator::class].createDestination().apply {
route = Graph.CustomDestination.route
}
การระบุอาร์กิวเมนต์ปลายทาง
คุณกำหนดอาร์กิวเมนต์ปลายทางให้เป็นส่วนหนึ่งของคลาสเส้นทางได้ สิ่งเหล่านี้สามารถเป็นได้ ในลักษณะเดียวกับที่คุณทำสำหรับคลาส Kotlin ทั่วไป อาร์กิวเมนต์ที่จำเป็นคือ กำหนดเป็นประเภทที่ไม่เป็นค่าว่างและอาร์กิวเมนต์ที่เลือกระบุโดยค่าเริ่มต้น
กลไกที่สำคัญสำหรับการแสดงเส้นทางและอาร์กิวเมนต์คือสตริง
อ้างอิง การใช้สตริงเพื่อสร้างรูปแบบเส้นทางจะช่วยให้สามารถจัดเก็บสถานะการนำทาง และ
กู้คืนจากดิสก์ในระหว่างการกำหนดค่า
การเปลี่ยนแปลง และกระบวนการที่เริ่มต้นโดยระบบ
การเสียชีวิต ด้วยเหตุนี้
อาร์กิวเมนต์การนำทางแต่ละรายการจะต้องทำให้เป็นอนุกรมได้ กล่าวคือ
ซึ่งจะแปลงการแสดงค่าอาร์กิวเมนต์ในหน่วยความจำเป็นค่าอาร์กิวเมนต์
String
การเรียงอันดับ Kotlin
ปลั๊กอิน
สร้างวิธีการทำให้ต่อเนื่องโดยอัตโนมัติสำหรับขั้นพื้นฐาน
เมื่อ
เพิ่มคำอธิบายประกอบ @Serializable
ลงในออบเจ็กต์แล้ว
@Serializable
data class MyRoute(
val id: String,
val myList: List<Int>,
val optionalArg: String? = null
)
fragment<MyFragment, MyRoute>
การระบุประเภทที่กำหนดเอง
สำหรับประเภทอาร์กิวเมนต์ที่กำหนดเอง คุณจะต้องระบุคลาส NavType
ที่กำหนดเอง ช่วงเวลานี้
ช่วยให้คุณควบคุมการแยกวิเคราะห์ประเภทของคุณจากเส้นทางหรือลิงก์ในรายละเอียดได้
ตัวอย่างเช่น เส้นทางที่ใช้กำหนดหน้าจอค้นหาอาจมีคลาสที่ แสดงพารามิเตอร์การค้นหา ดังนี้
@Serializable
data class SearchRoute(val parameters: SearchParameters)
@Serializable
data class SearchParameters(
val searchQuery: String,
val filters: List<String>
)
NavType
ที่กำหนดเองอาจเขียนเป็น
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)
}
}
จากนั้นจะใช้ใน Kotlin DSL ได้เช่นเดียวกับประเภทอื่นๆ
fragment<SearchFragment, SearchRoute> {
label = getString(R.string.plant_search_title)
typeMap = mapOf(typeOf<SearchParameters>() to SearchParametersType)
}
เมื่อนำทางไปยังจุดหมาย ให้สร้างอินสแตนซ์ของเส้นทางของคุณ
val params = SearchParameters("rose", listOf("available"))
navController.navigate(route = SearchRoute(params))
รับพารามิเตอร์จากเส้นทางในปลายทางได้
val searchRoute = navController().getBackStackEntry<SearchRoute>().toRoute<SearchRoute>()
val params = searchRoute.parameters
Deep Link
คุณเพิ่ม Deep Link ไปยังปลายทางใดก็ได้ เช่นเดียวกับที่เพิ่ม Deep Link ด้วยการใช้ XML กราฟการนำทาง ขั้นตอนเดียวกันทั้งหมดที่ระบุไว้ในหัวข้อการสร้าง Deep Link สําหรับปลายทางจะมีผลกับกระบวนการ ของการสร้าง Deep Link โดยใช้ Kotlin DSL
เมื่อสร้าง Implicit Deep Link
แต่คุณไม่มีทรัพยากรการนำทาง XML ที่สามารถวิเคราะห์
<deepLink>
องค์ประกอบ ดังนั้น คุณจึงไม่สามารถวาง <nav-graph>
ในไฟล์ AndroidManifest.xml
และต้องเพิ่ม intent แทน
ตัวกรองไปยังกิจกรรมของคุณด้วยตนเอง ความตั้งใจ
ตัวกรองที่คุณระบุควรตรงกับเส้นทางพื้นฐาน การกระทำ และประเภท MIME ของ
Deep Link ของแอปของคุณ
ระบบจะเพิ่ม Deep Link ไปยังปลายทางโดยการเรียกใช้ฟังก์ชัน deepLink
ภายใน
เรือแลมบ์ดาของปลายทาง ระบบยอมรับเส้นทางเป็นประเภทพารามิเตอร์ และ
พารามิเตอร์ basePath
สำหรับเส้นทางฐานของ URL ที่ใช้สำหรับ Deep Link
คุณยังสามารถเพิ่มการดำเนินการและ mimetype โดยใช้เมธอด
deepLinkBuilder
ตามหลัง lambda
ตัวอย่างต่อไปนี้สร้าง URI ของ Deep Link สำหรับปลายทาง 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/*"
}
}
รูปแบบ URI
รูปแบบ URI ของ Deep Link จะสร้างขึ้นโดยอัตโนมัติจากช่องของเส้นทาง โดยใช้กฎต่อไปนี้
- พารามิเตอร์ที่จําเป็นจะต่อท้ายเป็นพารามิเตอร์เส้นทาง (เช่น
/{id}
) - พารามิเตอร์ที่มีค่าเริ่มต้น (พารามิเตอร์ที่ไม่บังคับ) จะเพิ่มเป็น query
พารามิเตอร์ (เช่น
?name={name}
) - ระบบจะเพิ่มคอลเล็กชันต่อท้ายเป็นพารามิเตอร์การค้นหา (เช่น
?items={value1}&items={value2}
) - ลำดับของพารามิเตอร์ตรงกับลำดับของฟิลด์ในเส้นทาง
ตัวอย่างเช่น ประเภทเส้นทางต่อไปนี้
@Serializable data class PlantDetail(
val id: String,
val name: String,
val colors: List<String>,
val latinName: String? = null,
)
มีรูปแบบ URI ที่สร้างขึ้นเป็น
basePath/{id}/{name}/?colors={color1}&colors={color2}&latinName={latinName}
คุณเพิ่ม Deep Link ได้ไม่จำกัดจำนวน ทุกครั้งที่คุณโทร
deepLink()
Deep Link ใหม่จะต่อท้ายรายการที่เก็บไว้สำหรับปลายทางนั้น
ข้อจำกัด
ปลั๊กอิน Safe Args คือ
ไม่สามารถทำงานร่วมกับ Kotlin DSL เนื่องจากปลั๊กอินจะค้นหาไฟล์ทรัพยากร XML เพื่อทำสิ่งต่อไปนี้
สร้างชั้นเรียน Directions
และ Arguments