片段和 Kotlin DSL

導覽元件提供以 Kotlin 為基礎的網域特定語言;或 DSL,依賴 Kotlin 的類型安全 建構工具 ,直接在 Google Cloud 控制台實際操作。這個 API 可讓您在 Kotlin 程式碼中透過宣告方式製作圖表,而非 而不是 XML 資源中的如果您想建構應用程式的 以動態方式進行導覽舉例來說,您的應用程式可以下載 載入外部網路服務的導覽設定,然後再使用該設定 設定,以動態方式在活動的 onCreate() 函式。

依附元件

如要搭配使用 Kotlin DSL 與 Fragment,請將以下依附元件新增至應用程式的 build.gradle 檔案:

Groovy

dependencies {
    def nav_version = "2.8.0"

    api "androidx.navigation:navigation-fragment-ktx:$nav_version"
}

Kotlin

dependencies {
    val nav_version = "2.8.0"

    api("androidx.navigation:navigation-fragment-ktx:$nav_version")
}

建構圖表

以下是以 Sunflower 為基礎 app。為此 舉例來說,我們有兩個目的地:homeplant_detail。當使用者首次啟動應用程式時,會顯示 home 目的地。這個目的地會顯示使用者花園中的植物清單。當使用者選取其中一種植物時,應用程式會前往 plant_detail 目的地。

圖 1 顯示了這些目的地和 plant_detail 目的地所需的引數,以及應用程式用於從 home 導覽至 plant_detail 的動作 to_plant_detail

Sunflower 應用程式有兩個目的地,以及一個用於連接這二者的動作。
圖 1. Sunflower 應用程式有兩個目的地:homeplant_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)

定義路徑後,即可建構導覽圖。

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 建構工具函式。這個函式需要兩種 引數 ,直接在 Google Cloud 控制台實際操作。

首先,Fragment 類別 可為這個目的地提供 UI。設定效果的效果等同於 在定義的片段目的地上設定 android:name 屬性 以 XML 檔案形式提供內容

第二是路徑這必須是從 Any 延伸的可序列化類型。這項服務 應包含此目的地會使用的任何導覽引數; 及其類型

這個函式也接受選用的 lambda 來進行其他設定,例如: 做為目的地標籤,以及用於自訂的嵌入式建構工具函式 引數和深層連結

最後,您可以使用homeplant_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 針對三種目的地類型提供內建支援,這三種類型為 FragmentActivityNavGraph 目的地。每個目的地都有專屬的內嵌擴充功能,可用來建構及設定這個目的地。

片段目的地

fragment()敬上 DSL 函式可使用 UI 的片段類別和 用於明確識別這個目的地的路徑類型,後面接有 lambda 您可在其中提供額外設定,詳情請參閱 顯示比例。

fragment<MyFragment, MyRoute> {
   label = getString(R.string.fragment_title)
   // custom argument types, deepLinks
}

活動目的地

activity()敬上 DSL 函式會採用路線的 type 參數,但不會參數化為 任何實作活動類別。而是改為在activityClass 結尾的 lambda這種靈活度可讓您針對不同區域 應該使用 意圖 則不符合說明與片段目的地一樣 設定標籤、自訂引數和深層連結

activity<MyRoute> {
   label = getString(R.string.activity_title)
   // custom argument types, deepLinks...

   activityClass = MyActivity::class
}

navigation()敬上 DSL 函式可用於建立巢狀導覽 圖表。這個函式採用類型 參數,用於指派給這個圖表的路徑。此函式還使用兩個引數: 做為圖表起始目的地的路徑,以及進一步的 lambda 設定圖表有效元素包括其他目的地、自訂引數 類型、深層連結,以及廣告標記的 目的地。 透過 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)

您也可以使用一元加號運算子,將新建構的目的地直接新增至圖表:

// 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

深層連結

深層連結可以加入任何目的地,就像使用以 XML 為基礎的連結一樣 導覽圖與「建立深層連結」一文中定義的所有程序相同 如果是目的地,則適用於 這堂課程說明如何使用 Kotlin DSL 建立深層連結。

建立隱含深層連結時 但這沒有可分析的 XML 導覽資源 <deepLink> 元素。因此,你無法只將 <nav-graph> 元素中,AndroidManifest.xml則必須改為在意圖中加入意圖 篩選器,手動套用到活動。意圖 您提供的篩選器必須符合 追蹤應用程式的深層連結

如要將深層連結新增至目的地,請在該位置呼叫 deepLink 函式 目的地的 lambda。它接受路徑做為參數化類型,以及 用於深層連結的網址基本路徑參數 basePath

您也可以使用 deepLinkBuilder敬上 結尾的 lambda。

以下範例會建立 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 資源檔案來產生 DirectionsArguments 類別。