在目的地之間傳遞資料

導覽功能可讓您定義目的地的引數,將資料附加至導覽作業。例如,使用者設定檔目的地可能會利用使用者 ID 引數來判斷要顯示的使用者。

一般而言,在目的地之間傳遞的資料量越低越好。例如,您應該傳遞擷取物件的金鑰,而不是傳遞物件本身,因為 Android 上用於所有儲存狀態的總空間有限。如果您需要傳遞大量資料,請使用 ViewModel 總覽中所述的 ViewModel

定義目的地引數

如要在目的地之間傳遞資料,請先按照下列步驟將引數加入接收目的地的引數,藉此定義引數:

  1. 導覽編輯器中,按一下接收引數的目的地。
  2. 在「Attributes」面板中按一下「Add」 (+)。
  3. 在出現的「Add Argument Link」視窗中輸入引數名稱、引數類型、引數是否可為空值和預設值 (如有需要)。
  4. 按一下「Add」。請注意,引數現在會顯示在「Attributes」面板的「Arguments」清單中。
  5. 接著,按一下會將您帶至此目的地的對應動作。在「Attributes」面板中,現在應該會在「Argument Default Values」部分看到新增的引數。
  6. 您也可以看到引數新增到 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.7.7"
        classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version"
    }
}

Kotlin

buildscript {
    repositories {
        google()
    }
    dependencies {
        val nav_version = "2.7.7"
        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 傳遞到起始目的地:

如要擷取起始目的地中的資料,請呼叫 Fragment.getArguments()

ProGuard 注意事項

如果您要縮減程式碼,必須防止 ParcelableSerializableEnum 類別名稱在壓縮過程中模糊化。您可以選擇以下其中一種方法:

  • 使用 @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

...

其他資源

如要進一步瞭解導航,請參閱下列其他資源。

範例

程式碼研究室

影片