導覽元件提供以 Kotlin 為基礎的網域專屬語言 (即 DSL),而且必須依賴 Kotlin 的類型安全建構工具來建立資料結構。這個 API 可讓您在 Kotlin 程式碼中透過宣告方式製作圖表,而非
而不是 XML 資源中的如果您想建構應用程式的
以動態方式進行導覽例如,應用程式可從外部網路服務下載及快取導覽設定,然後使用該設定在活動的 onCreate()
函式中動態建構導覽圖。
依附元件
如要搭配使用 Kotlin DSL 與 Fragment,請將以下依附元件新增至應用程式的
build.gradle
檔案:
Groovy
dependencies { def nav_version = "2.9.3" api "androidx.navigation:navigation-fragment-ktx:$nav_version" }
Kotlin
dependencies { val nav_version = "2.9.3" api("androidx.navigation:navigation-fragment-ktx:$nav_version") }
建構圖表
以下是基於 Sunflower 應用程式的基本範例。在這個範例中,有兩個目的地:home
和 plant_detail
。當使用者首次啟動應用程式時,會顯示 home
目的地。這個目的地會顯示使用者花園中的植物清單。當使用者選取其中一種植物時,應用程式會前往 plant_detail
目的地。
圖 1 顯示了這些目的地和 plant_detail
目的地所需的引數,以及應用程式用於從 home
導覽至 plant_detail
的動作 to_plant_detail
。

home
和 plant_detail
,以及一個用於連接這二者的動作。託管 Kotlin DSL 導覽圖
建構應用程式的導覽圖之前,需要一個代管
圖表。這個範例使用了片段,因此會將圖表託管在 FragmentContainerView
內的 NavHostFragment
中:
<!-- 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 中,會有一個動作將目的地 ID 和一個或多個引數連結在一起。不過,使用導覽 DSL 時,路徑可以包含引數,當做路徑的一部分,代表使用 DSL 時沒有動作的概念。
下一步是定義定義圖表時要使用的路徑。
為圖表建立路線
系統會將基於 XML 的導覽圖剖析為一部分
Android 建構程序的各個環節系統會為每個 id
建立數字常數
屬性。這些建構時間產生的靜態 ID
在執行階段建構導覽圖時可使用,以便導覽 DSL
使用 serializable
Type,而非
而非客戶 ID每條路線都以不重複的類型表示。
處理引數時,這些常數會建構至路徑類型中。這樣就能確保類型安全 做為導覽引數
@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()
DSL 建構工具函式定義了兩個片段目的地。這個函式需要兩個類型引數。
首先,Fragment
類別
可為這個目的地提供 UI。設定效果的效果等同於
在定義的片段目的地上設定 android:name
屬性
以 XML 檔案形式提供內容
第二是路徑此類型必須是可序列化的類型,且會從 Any
擴充。這項服務
應包含此目的地會使用的任何導覽引數;
及其類型
此函式也接受選用的 lambda 來進行其他設定 (例如目的地標籤),以及用於自訂引數和深層連結的嵌入式建構工具函式。
使用 Kotlin DSL 圖表進行導覽
最後,您可以使用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 針對三種目的地類型提供內建支援,這三種類型為 Fragment
、Activity
和 NavGraph
目的地。每個目的地都有專屬的內嵌擴充功能,可用來建構及設定這個目的地。
片段目的地
fragment()
DSL 函式可經過參數化處理,轉換為 UI 的片段類別,以及用於唯一識別此目的地的路徑類型,目的地後方會接 lambda 函式,您可在該函式中提供其他設定,如「使用 Kotlin DSL 圖表進行導覽」一節所述。
fragment<MyFragment, MyRoute> {
label = getString(R.string.fragment_title)
// custom argument types, deepLinks
}
活動目的地
activity()
DSL 函式會採用路線的 type 參數,但不會參數化為
任何實作活動類別。您可在結尾的 lambda 中設定選用的 activityClass
。這種靈活度可讓您針對不同區域
應該使用
意圖
則不符合說明與片段目的地一樣,您也可以設定標籤、自訂引數和深層連結。
activity<MyRoute> {
label = getString(R.string.activity_title)
// custom argument types, deepLinks...
activityClass = MyActivity::class
}
導覽圖目的地
navigation()
DSL 函式可用於建立巢狀導覽
圖表。這個函式採用類型
參數,用於指派給這個圖表的路徑。它也使用兩個引數:圖表的起始目的地路徑,以及用於進一步設定圖表的 lambda。有效的元素包括其他目的地、自訂引數類型、深層連結,以及目的地的描述性標籤。在使用 NavigationUI
將導覽圖繫結至 UI 元件時,這個標籤將大有用處。
@Serializable data object HomeGraph
@Serializable data object Home
navigation<HomeGraph>(startDestination = Home) {
// label, other destinations, deep links
}
支援自訂目的地
如果您使用無法直接支援 Kotlin DSL 的新目的地類型,則可使用 addDestination()
將這些目的地新增至您的 Kotlin DSL:
// The NavigatorProvider is retrieved from the NavController
val customDestination = navigatorProvider[CustomNavigator::class].createDestination().apply {
route = Graph.CustomDestination.route
}
addDestination(customDestination)
您也可以使用一元加號運算子,將新建構的目的地直接新增至圖表:
// The NavigatorProvider is retrieved from the NavController
+navigatorProvider[CustomNavigator::class].createDestination().apply {
route = Graph.CustomDestination.route
}
提供目的地引數
目的地引數可定義為路徑類別的一部分。您可以輸入 定義的方法與對任何 Kotlin 類別相同必要引數會定義為非空值類型,選用引數則會定義為預設值。
表示路徑及其引數的基礎機制是字串
基於這個原因。使用字串建立路徑模型可讓系統儲存導覽狀態,
在設定期間從磁碟還原
變更和系統啟動的程序
死亡。因此,每個導覽引數都必須可序列化,也就是說,它應具備將引數值的記憶體內表示法轉換為 String
的方法。
當 @Serializable
註解新增至物件時,Kotlin 序列化外掛程式會自動為基本類型產生序列化方法。
@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
@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
深層連結
深層連結可以加入任何目的地,就像使用以 XML 為基礎的連結一樣 導覽圖「為目的地建立深層連結」一文中定義的所有相同程序,皆適用於使用 Kotlin DSL 建立深層連結的程序。
不過,在建立隱含深層連結時,您沒有可針對 <deepLink>
元素進行分析的 XML 導覽資源。因此,您無法將 <nav-graph>
元素放入 AndroidManifest.xml
檔案,而必須手動將意圖篩選器新增至活動。意圖
您提供的篩選器必須符合
追蹤應用程式的深層連結
如要將深層連結新增至目的地,請在該位置呼叫 deepLink
函式
目的地的 lambda。它會將路徑做為參數化類型,以及參數 basePath
做為用於深層連結的網址基準路徑。
您也可以使用 deepLinkBuilder
尾隨 lambda 新增動作和 mimetype。
以下範例會為 Home
目的地建立深層連結 URI。
@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 格式:
- 必要參數會以路徑參數的形式附加 (例如:
/{id}
) - 含有預設值的參數 (選用參數) 會以查詢參數的形式附加 (例如:
?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}
可新增的深層連結數量沒有上限。每次呼叫 deepLink()
時,都會有一個新的深層連結附加至由該目的地維護的清單。
限制
Safe Args 外掛程式與 Kotlin DSL 不相容,因為外掛程式會尋找 XML 資源檔案來產生 Directions
和 Arguments
類別。