在目的地之間傳遞資料

Navigation 可讓您定義引數,將資料附加至導覽作業 字串。舉例來說,使用者設定檔目的地可能會採用使用者 ID 引數以決定要顯示的使用者。

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

定義目的地引數

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

  1. Navigation 編輯器中: 請按一下接收引數的目的地。
  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 和 可序列化,如上表所示。 如要傳遞自訂複雜資料,請將資料儲存在其他位置 (例如 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.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")
    }
}

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

...

其他資源

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

範例

程式碼研究室

影片