ส่วนประกอบการนําทางมีภาษาเฉพาะโดเมนหรือ DSL ที่ใช้ Kotlin ซึ่งอาศัยตัวสร้างที่ปลอดภัยจากประเภทของ Kotlin API นี้ช่วยให้คุณเขียนกราฟในโค้ด Kotlin ได้อย่างชัดเจนแทน
มากกว่าภายในทรัพยากร XML ซึ่งจะเป็นประโยชน์หากคุณต้องการสร้างแอป
การนำทางแบบไดนามิก เช่น แอปอาจดาวน์โหลดและแคชการกำหนดค่าการนําทางจากเว็บเซอร์วิสภายนอก จากนั้นใช้การกําหนดค่านั้นเพื่อสร้างกราฟการนําทางแบบไดนามิกในฟังก์ชันonCreate()
ของกิจกรรม
ทรัพยากร Dependency
หากต้องการใช้ Kotlin DSL กับ Fragments ให้เพิ่มทรัพยากร Dependency ต่อไปนี้ในส่วน
build.gradle
ไฟล์:
Groovy
dependencies { def nav_version = "2.9.2" api "androidx.navigation:navigation-fragment-ktx:$nav_version" }
Kotlin
dependencies { val nav_version = "2.9.2" api("androidx.navigation:navigation-fragment-ktx:$nav_version") }
การสร้างกราฟ
ต่อไปนี้เป็นตัวอย่างพื้นฐานที่อิงตามแอป Sunflower ในตัวอย่างนี้เรามีปลายทาง 2 แห่ง ได้แก่ home
และ plant_detail
home
ปลายทาง จะแสดงเมื่อผู้ใช้เปิดแอปเป็นครั้งแรก จุดหมายนี้
แสดงรายการต้นไม้จากสวนของผู้ใช้ เมื่อผู้ใช้เลือกพืชชนิดใดชนิดหนึ่ง แอปจะไปยังปลายทาง plant_detail
รูปที่ 1 แสดงปลายทางเหล่านี้พร้อมกับอาร์กิวเมนต์ที่แอตทริบิวต์
ปลายทาง plant_detail
และการดำเนินการ to_plant_detail
ที่แอปใช้
เพื่อนำทางจาก home
ไป 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 การนำทาง
ใช้ ซีเรียลได้
ประเภท แทนที่จะเป็น
รหัส แต่ละเส้นทางจะแสดงด้วยประเภทที่ไม่ซ้ำกัน
เมื่อจัดการกับอาร์กิวเมนต์ ระบบจะฝังอาร์กิวเมนต์ไว้ในประเภทเส้นทาง ซึ่งช่วยให้คุณมีความปลอดภัยของประเภทสำหรับอาร์กิวเมนต์การนําทาง
@Serializable data object Home
@Serializable data class Plant(val id: String)
สร้างกราฟด้วย NavGraphBuilder DSL
เมื่อกำหนดเส้นทางแล้ว คุณสามารถสร้างกราฟการนำทางได้
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 คือเส้นทาง ต้องเป็นประเภทที่ serialize ได้ซึ่งขยายมาจาก 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
กับ NavBackStackEntry
เพื่อรับอินสแตนซ์เส้นทาง
val plantDetailRoute = findNavController().getBackStackEntry<PlantDetail>().toRoute<PlantDetail>()
val plantId = plantDetailRoute.id
หาก PlantDetailFragment
ใช้ ViewModel
ให้รับอินสแตนซ์เส้นทางโดยใช้ SavedStateHandle.toRoute
val plantDetailRoute = savedStateHandle.toRoute<PlantDetail>()
val plantId = plantDetailRoute.id
ส่วนที่เหลือของคู่มือนี้จะอธิบายองค์ประกอบกราฟการไปยังส่วนต่างๆ, ปลายทาง, และวิธีใช้เมื่อสร้างกราฟ
จุดหมาย
DSL ของ Kotlin รองรับปลายทาง 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 เพื่อไปให้ไกลขึ้น
กำหนดค่ากราฟ องค์ประกอบที่ถูกต้อง ได้แก่ ปลายทางอื่นๆ ประเภทอาร์กิวเมนต์ที่กำหนดเอง Deep Link และป้ายกำกับที่สื่อความหมายสำหรับปลายทาง
ป้ายกำกับนี้มีประโยชน์สำหรับการเชื่อมโยงกราฟการนำทางกับคอมโพเนนต์ 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 ทั่วไป อาร์กิวเมนต์ที่ต้องระบุจะกำหนดเป็นประเภทที่ไม่ใช่ Null และอาร์กิวเมนต์ที่ไม่บังคับจะกำหนดด้วยค่าเริ่มต้น
กลไกพื้นฐานในการแสดงเส้นทางและอาร์กิวเมนต์ของเส้นทางจะอิงตามสตริง การใช้สตริงเพื่อจำลองเส้นทางช่วยให้จัดเก็บและกู้คืนสถานะการนําทางจากดิสก์ได้ในระหว่างการเปลี่ยนแปลงการกําหนดค่าและการสิ้นสุดกระบวนการที่ระบบเริ่ม ด้วยเหตุนี้ อาร์กิวเมนต์การนําทางแต่ละรายการจึงต้องจัดทําเป็นอนุกรมได้ กล่าวคือ ควรมีเมธอดที่แปลงการนําเสนอค่าอาร์กิวเมนต์ในหน่วยความจําเป็น String
การเรียงอันดับ Kotlin
ปลั๊กอิน
สร้างวิธีการทำให้ต่อเนื่องโดยอัตโนมัติสำหรับขั้นพื้นฐาน
เมื่อ
เพิ่มคำอธิบายประกอบ @Serializable
ลงในออบเจ็กต์แล้ว
@Serializable
data class MyRoute(
val id: String,
val myList: List<Int>,
val optionalArg: String? = null
)
fragment<MyFragment, MyRoute>
การระบุประเภทที่กำหนดเอง
สำหรับประเภทอาร์กิวเมนต์ที่กำหนดเอง คุณจะต้องระบุคลาส NavType
ที่กำหนดเอง ซึ่งจะช่วยให้คุณควบคุมวิธีที่ระบบจะแยกวิเคราะห์ประเภทของคุณจากเส้นทางหรือ Deep Link ได้
ตัวอย่างเช่น เส้นทางที่ใช้กําหนดหน้าจอการค้นหาอาจมีคลาสที่แสดงพารามิเตอร์การค้นหา ดังนี้
@Serializable
data class SearchRoute(val parameters: SearchParameters)
@Serializable
@Parcelize
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>(
typeMap = mapOf(typeOf<SearchParameters>() to SearchParametersType)
) {
label = getString(R.string.plant_search_title)
}
เมื่อนำทางไปยังจุดหมาย ให้สร้างอินสแตนซ์ของเส้นทางของคุณ
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
อย่างไรก็ตาม เมื่อสร้าง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