導覽可為目的地定義引數,將資料附加至導覽作業。舉例來說,使用者設定檔目的地可能會利用使用者 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 直接傳遞資料。
To add Safe Args
to your project, include the following classpath
in your top level build.gradle
file:
Groovy
buildscript { repositories { google() } dependencies { def nav_version = "2.8.5" classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version" } }
Kotlin
buildscript { repositories { google() } dependencies { val nav_version = "2.8.5" classpath("androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version") } }
You must also apply one of two available plugins.
To generate Java language code suitable for Java or mixed Java and Kotlin modules, add
this line to your app or module's build.gradle
file:
Groovy
plugins { id 'androidx.navigation.safeargs' }
Kotlin
plugins { id("androidx.navigation.safeargs") }
Alternatively, to generate Kotlin code suitable for Kotlin-only modules add:
Groovy
plugins { id 'androidx.navigation.safeargs.kotlin' }
Kotlin
plugins { id("androidx.navigation.safeargs.kotlin") }
You must have android.useAndroidX=true
in your
gradle.properties
file as per
Migrating to AndroidX.
啟用 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
...
其他資源
如要進一步瞭解導航,請參閱下列其他資源。