導覽可為目的地定義引數,將資料附加至導覽作業。舉例來說,使用者設定檔目的地可能會利用使用者 ID 引數來判斷要顯示的使用者。
一般而言,在目的地之間傳遞的資料量越低越好。例如,您應該傳遞擷取物件的金鑰,而不是傳遞物件本身,因為 Android 上用於所有儲存狀態的總空間有限。如果您需要傳遞大量資料,請使用 ViewModel 簡介中所述的 ViewModel
。
定義目的地引數
如要在目的地之間傳遞資料,請先按照下列步驟將引數加入接收目的地的引數,藉此定義引數:
- 在導覽編輯器中,按一下接收引數的目的地。
- 在「Attributes」面板中按一下「Add」 (+)。
- 在出現的「Add Argument Link」視窗中輸入引數名稱、引數類型、引數是否可為空值和預設值 (如有需要)。
- 按一下「Add」。請注意,引數現在會顯示在「Attributes」面板的「Arguments」清單中。
- 接著,按一下會將您帶至此目的地的對應動作。在「Attributes」面板中,現在應該會在「Argument Default Values」部分看到新增的引數。
您也可以看到引數新增到 XML 中。按一下「Text」分頁標籤切換至 XML 檢視畫面,就會發現引數已新增至接收該引數的目的地。以下範例說明:
<fragment android:id="@+id/myFragment" > <argument android:name="myArg" app:argType="integer" android:defaultValue="0" /> </fragment>
支援的引數類型
導覽程式庫支援下列引數類型:
類型 | app:argType 語法 | 支援預設值 | 由路徑處理 | 可以為空值 |
---|---|---|---|---|
整數 | app:argType="integer" | 是 | 是 | 否 |
浮點數 | app:argType="float" | 是 | 是 | 否 |
長整數 | app:argType="long" | 是 - 預設值一律必須以「L」結尾 (例如「123L」)。 | 是 | 否 |
布林值 | app:argType="boolean" | 是 - 「true」或「false」 | 是 | 否 |
字串 | app:argType="string" | 是 | 有 | 是 |
資源參照 | app:argType="reference" | 是 - 預設值必須是「@resourceType/resourceName」格式 (例如「@style/myCustomStyle」) 或「0」 | 是 | 否 |
自訂 Parcelable | app:argType="<type>",其中 <type> 是 Parcelable 的完整類別名稱 |
支援預設值「@null」。不支援其他預設值。 | 否 | 是 |
自訂 Serializable | app:argType="<type>",其中 <type> 是 Serializable 的完整類別名稱 |
支援預設值「@null」。不支援其他預設值。 | 否 | 是 |
自訂列舉 | app:argType="<type>",其中 <type> 是列舉的完整名稱 | 是 - 預設值必須與不合格名稱對應 (例如「SUCCESS」對應 MyEnum.SUCCESS)。 | 否 | 否 |
如果引數類型支援空值,您可以使用 android:defaultValue="@null"
宣告預設空值。
您可以從字串剖析路徑、深層連結和 URI 及其引數,但無法使用 Parcelable 和 Serializable 等自訂資料類型,如上表所述。如要傳遞自訂複雜資料,請將資料儲存在其他位置 (例如 ViewModel 或資料庫),且只在導覽時傳遞 ID,然後在導覽後在新位置擷取資料。
當您選擇其中一種自訂類型時,系統會顯示「Select Class」對話方塊,並提示您選擇該類型對應的類別。「Project」分頁可讓您從目前的專案中選擇類別。
您可以選擇 <inferred type>,讓導覽程式庫根據提供的值判斷類型。
您可以勾選「Array」,表示引數應為所選「Type」值的陣列。請注意:
- 不支援列舉陣列和資訊參照陣列。
- 無論基礎類型是否支援可為空值的值,陣列提供支援該值。例如,使用
app:argType="integer[]"
時,您可以利用app:nullable="true"
表示可以接受傳遞空值陣列。 - 陣列支援單一預設值「@null」。陣列不支援任何其他預設值。
覆寫動作中的目的地引數
所有導覽至目的地的動作都會使用目的地層級的引數和預設值。如有需要,您可以在動作層級定義引數,覆寫引數的預設值;如果沒有引數,您也可以設定一個。這個引數的名稱和類型必須與目的地中宣告的引數相同。
下列 XML 宣告使用引數覆寫上述範例所示目的地層級引數的動作:
<action android:id="@+id/startMyFragment"
app:destination="@+id/myFragment">
<argument
android:name="myArg"
app:argType="integer"
android:defaultValue="1" />
</action>
使用 Safe Args 傳遞類型安全的資料
導覽元件具備稱為 Safe Args 的 Gradle 外掛程式,可產生簡單的物件和建構工具類別,用於對關聯的引數進行類型安全存取。強烈建議您使用 Safe Args 進行導覽和傳遞資料,才可以確保類型安全。
如果您使用的不是 Gradle,則無法使用 Safe Args 外掛程式。在這種情況下,您可以使用 Bundle 直接傳遞資料。
如要在專案中新增 Safe Args,請在頂層 build.gradle
檔案中納入下列 classpath
:
Groovy
buildscript { repositories { google() } dependencies { def nav_version = "2.8.4" classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version" } }
Kotlin
buildscript { repositories { google() } dependencies { val nav_version = "2.8.4" classpath("androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version") } }
您也必須從兩個可用的外掛程式之中選擇一項套用。
如要產生適合 Java 或混用 Java 及 Kotlin 模組的 Java 語言程式碼,請將以下這一行新增至應用程式或模組的 build.gradle
檔案:
Groovy
plugins { id 'androidx.navigation.safeargs' }
Kotlin
plugins { id("androidx.navigation.safeargs") }
或者,如要產生適合 Kotlin 模組的 Kotlin 程式碼,請加入:
Groovy
plugins { id 'androidx.navigation.safeargs.kotlin' }
Kotlin
plugins { id("androidx.navigation.safeargs.kotlin") }
根據遷移至 AndroidX 一文,gradle.properties
檔案中必須含有 android.useAndroidX=true
。
啟用 Safe Args 之後,產生的程式碼會包含下列個別動作的類型安全類別和方法,以及每個傳送和接收的目的地。
系統會為每個動作最初的目的地建立類別。此類別的名稱是來源目的地的名稱加上「Directions」。例如,如果來源目的地是名為
SpecifyAmountFragment
的片段,產生的類別名稱為SpecifyAmountFragmentDirections
。此類別會為來源目的地中定義的每個動作提供方法。
針對用來傳遞引數的每個動作,都會建立一個內部類別,其名稱根據動作而定。例如,如果動作稱為
confirmationAction,
,類別則稱為ConfirmationAction
。如果您的動作包含不含defaultValue
的引數,您可以使用關聯的動作類別來設定引數值。系統會為接收目的地建立類別。此類別的名稱是目的地的名稱加上「Args」。例如,如果目的地片段的名稱是
ConfirmationFragment,
,則產生的類別名稱為ConfirmationFragmentArgs
。請使用此類別的fromBundle()
方法擷取引數。
以下範例說明如何使用這些方法來設定引數,並將其傳遞至 navigate()
方法:
Kotlin
override fun onClick(v: View) { val amountTv: EditText = view!!.findViewById(R.id.editTextAmount) val amount = amountTv.text.toString().toInt() val action = SpecifyAmountFragmentDirections.confirmationAction(amount) v.findNavController().navigate(action) }
Java
@Override public void onClick(View view) { EditText amountTv = (EditText) getView().findViewById(R.id.editTextAmount); int amount = Integer.parseInt(amountTv.getText().toString()); ConfirmationAction action = SpecifyAmountFragmentDirections.confirmationAction(); action.setAmount(amount); Navigation.findNavController(view).navigate(action); }
在接收目的地的程式碼中,使用 getArguments()
方法擷取組合並使用其中的內容。使用 -ktx
依附元件時,Kotlin 使用者也可以使用 by navArgs()
屬性委派來存取引數。
Kotlin
val args: ConfirmationFragmentArgs by navArgs() override fun onViewCreated(view: View, savedInstanceState: Bundle?) { val tv: TextView = view.findViewById(R.id.textViewAmount) val amount = args.amount tv.text = amount.toString() }
Java
@Override public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { TextView tv = view.findViewById(R.id.textViewAmount); int amount = ConfirmationFragmentArgs.fromBundle(getArguments()).getAmount(); tv.setText(amount + ""); }
結合全域動作使用 Safe Args
使用 Safe Args 搭配全域動作 時,您必須為根層級值 <navigation>
元素提供 android:id
值,如以下範例所示:
<?xml version="1.0" encoding="utf-8"?> <navigation xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/main_nav" app:startDestination="@id/mainFragment"> ... </navigation>
導覽針對以 android:id
值為根據的 <navigation>
元素產生 Directions
類別。例如,如果您有一個帶有 android:id=@+id/main_nav
的 <navigation>
元素,產生的類別名稱為 MainNavDirections
。<navigation>
元素中的所有目的地都已產生方法,以上一節所述的方法存取所有關聯的全域動作。
使用 Bundle 物件在目的地之間傳遞資料
如果您使用的不是 Gradle,仍然可以使用 Bundle
物件,在目的地之間傳遞引數。建立 Bundle
物件,並使用 navigate()
將其傳遞至目的地,如以下範例所示:
Kotlin
val bundle = bundleOf("amount" to amount) view.findNavController().navigate(R.id.confirmationAction, bundle)
Java
Bundle bundle = new Bundle(); bundle.putString("amount", amount); Navigation.findNavController(view).navigate(R.id.confirmationAction, bundle);
在接收目的地的程式碼中,使用 getArguments()
方法擷取 Bundle
並使用其中的內容:
Kotlin
val tv = view.findViewById<TextView>(R.id.textViewAmount) tv.text = arguments?.getString("amount")
Java
TextView tv = view.findViewById(R.id.textViewAmount); tv.setText(getArguments().getString("amount"));
將資料傳遞到起始目的地
您可以將資料傳遞到應用程式的起始目的地。首先,您必須明確建構保存資料的 Bundle
。接下來,請使用下列其中一種方法,將 Bundle
傳遞至起始目的地:
- 如果您是透過程式輔助方式建立
NavHost
,請呼叫NavHostFragment.create(R.navigation.graph, args)
,其中args
是用來存放資料的Bundle
。 - 或者,您可以呼叫下列其中一個
NavController.setGraph()
超載來設定起始目的地引數:
如要擷取起始目的地中的資料,請呼叫 Fragment.getArguments()
。
ProGuard 注意事項
如果您要縮減程式碼,必須防止 Parcelable
、Serializable
和 Enum
類別名稱在壓縮過程中混淆您可以選擇使用以下其中一種方法:
- 使用 @Keep 註解。
- 使用 keepname 規則。
以下各小節將概略說明這些做法。
使用 @Keep 註解
以下範例會將 @Keep
備註新增至模型類別定義:
Kotlin
@Keep class ParcelableArg : Parcelable { ... } @Keep class SerializableArg : Serializable { ... } @Keep enum class EnumArg { ... }
Java
@Keep public class ParcelableArg implements Parcelable { ... } @Keep public class SerializableArg implements Serializable { ... } @Keep public enum EnumArg { ... }
使用 keepname 規則
此外,您也可以將 keepnames
規則新增至 proguard-rules.pro
檔案,如以下範例所示:
proguard-rules.pro
...
-keepnames class com.path.to.your.ParcelableArg
-keepnames class com.path.to.your.SerializableArg
-keepnames class com.path.to.your.EnumArg
...
其他資源
如要進一步瞭解導航,請參閱下列其他資源。