<h1>Android 筆記 – Navigation (二) 使用 Safe Args 傳遞資料</h1>
>前置閱讀:[Android筆記–Navigation導航組件](https://hackmd.io/@9YAtszqXS2OLNZOrLY_-Jg/android_navigation)
==<code>SafeArgs</code> 專門用來在 Navigation 中參數傳遞,並且同時能提供類型安全和編譯時檢查==,是 <code>Android Jetpack Navigation 組件</code> 的擴展,旨在增強 Fragment 或 Activity 之間導航時的類型安全性。==它利用生成的類型安全類別來取代手動構建 Bundle,從而減少錯誤並提高可讀性與可維護性==。
<font size=4><b>< <u>特點</u> ></b></font>
- <u>類型安全</u>: <code>SafeArgs</code> 生成的代碼會自動檢查參數的類型,避免了傳遞錯誤類型或因手動鍵入參數名稱錯誤而引發的崩潰
- <u>編譯時檢查</u>: <code>SafeArgs</code> 會在編譯時檢查導航操作和參數設置是否正確,減少因執行時錯誤造成的問題。
- <u>自動生成代碼</u>: 通過 <code>SafeArgs</code>,Android Studio 會自動生成導航操作和參數的代碼,大幅減少手動處理的工作量。
<font size=4><b>< <u>工作原理</u> ></b></font>
- <code>SafeArgs</code> 會為每個動作產生的目的地產生類別。產生的類別名稱會在原始目的地的類別名稱中加入「Directions」(路線)。舉例來說:
- 如果原始目的地的名稱為 「AAAFragment」
↓
- 則產生的類別名稱為 「AAAFragmentDirections」。
- 產生的類別會為原始目的地中定義的每種動作提供靜態方法。此方法會將任何已定義的動作參數當做引數,並傳回一個 <code>NavDirections 物件</code>,您就能將此物件直接傳遞到 <code>navigate()</code>。
- 命名法類似於 <code>ViewBinding</code>
| 特性 | <code>ViewBinding</code> | <code>SafeArgs</code> |
| --- | --- | --- |
| **功能** | 自動生成與 XML 佈局檔案相關的類別,用於綁定 View | 自動生成與導航動作及參數相關的類別,用於導航 |
| **生成邏輯** | 生成名稱為 `{佈局名稱 + Binding}` 的類別 | - 生成名稱為 `{Fragment名稱 + Directions}` 的類別。</br> - 生成名稱為 `{Fragment名稱 + Args}` 的類別|
- `{Fragment名稱 + Directions}`: 根據導航途中的`<action>標籤`生成,用於建立導航動作和設定參數
- `{Fragment名稱 + Args}`: 根據目的地是否需要接收參數來生成,用於在目的地 Fragment 中獲取參數
---
▼宣告依賴項
---
>[!Warning]注意事項
><code>SafeArgs</code> 需要分別在 <code>頂層 build.gradle</code> 和 <code>應用程式層的 build.gradle</code> 寫入依賴項
到官網尋找依賴項[AndroidDeveloper--Safe Args
](https://developer.android.com/guide/navigation/use-graph/safe-args?hl=zh-tw#enable)
<details><summary>圖解</summary>
<div>
```groovy=
//頂層模組的依賴項
buildscript {
repositories {
google()
}
dependencies {
val nav_version = "2.8.0"
classpath("androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version")
}
}
```
```groovy=
//應用程式層級的依賴項,二擇一即可。
//產生適合Kotlin 模組的 Kotlin 語言程式碼
plugins {
id("androidx.navigation.safeargs.kotlin")
}
//產生適合 Java 或混用 Java 及 Kotlin 模組的 Java 語言程式碼
plugins {
id("androidx.navigation.safeargs")
}
```

</div>
</details>
<br>
>延伸閱讀:[Android筆記–SafeArgs補充–為甚麼SafeArgs的依賴要用到ClassPath?
](https://hackmd.io/@9YAtszqXS2OLNZOrLY_-Jg/android_Navigation_SafeArgs_AdditionalInformation)
---
▼簡單使用
---
<code>SafeArgs</code> 的使用要點主要有三部分:
<details><summary>1. 導航圖設定</summary>
<div>
<br>
首先到導航圖中設定目標 Fragment 所要接收的參數
---
1. 到導航圖中選擇一個要作為目標的 Fragment,在 Attribute 面板中的 Arguments 中點選加號後進行設定

2. 設定完成後,Arguments 就會被附加到導航圖中的目標 Fragment 的設定之中

>[!Important]
>- 這個設定是多對一的,也就是說,所有「目的地為此 Fragment 的 起點 Fragment」,都必須要提交我們設定在目標 Fragment 上的 Argument 才能過來
>- 例如: 只要是以 FragmentB 為目標的 Fragment(例如: FragmentA、C、D),都必須提供一個 <code>String 參數</code> <code>nameBBB</code>,否則會出現錯誤

</div>
</details>
<details><summary>2. 在起點 Fragment 中放入參數</summary>
<div>
<br>
在起點 Fragment 中呼叫 「XXXDirections」,並選擇對應的 「actionXXToXX」,然後填入 「步驟1」 中所設計的參數,完成後,可將此物件提供給 <code>NavController</code> 當作 [Action](https://hackmd.io/@9YAtszqXS2OLNZOrLY_-Jg/android_navigation#NavigationGraphBasicProperties) 使用
---

```kotlin=
class FragmentA : Fragment() {
private lateinit var binding: FragmentABinding
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,savedInstanceState: Bundle?): View? {
binding = FragmentABinding.inflate(inflater, container, false)
val navController = findNavController()
val action = FragmentADirections.actionFragmentAToFragmentB("String A To B")
binding.button.setOnClickListener {
navController.navigate(action)
}
return binding.root
}
}
```
</div>
</details>
<details><summary>3. 在目標 Fragment 中接收參數</summary>
<div>
<br>
呼叫 「<code>Fragment 名 + Args 物件</code>」,然後使用其中的 <code>fromBundle(requireArguments())</code> 來獲取 <code>Arg</code>,再從 <code>Arg</code> 中獲取需要的參數
---

```kotlin=
class FragmentB : Fragment() {
private lateinit var binding: FragmentBBinding
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,avedInstanceState: Bundle?): View? {
binding = FragmentBBinding.inflate(inflater, container, false)
val arg = FragmentBArgs.fromBundle(requireArguments())
binding.tvBB.text = arg.nameBBB
return binding.root
}
}
```
</div>
</details>
---
▼Q&A
---
<details><summary>如果我 A 有設定 <code>Argument</code>,然後從 B 來 <code>popUpToA</code> 的話,這樣 A 可以拿到資料嗎?</summary>
<div>
---
不行,因為 <code>popUpTo</code> 的本質是清空導航堆疊的一部分,並彈回到指定的目的地,這個操作並不涉及資料傳遞。
</div>
</details>
---
▼GitHub 專案
---
[MyPratice_Navigation_SafeArgs_1](https://github.com/PudCheetah/MyPratice_Navigation_SafeArgs_1)
---
- 上一篇: [Android筆記–Navigation(一)導航組件](https://hackmd.io/@9YAtszqXS2OLNZOrLY_-Jg/android_navigation)
- 下一篇: [Adnroid筆記--Navigation(三)AppBarConfiguration](https://hackmd.io/@9YAtszqXS2OLNZOrLY_-Jg/android_AppBarConfiguration)
---
▼參考資料
---
- [AndroidDeveloper--Safe Args](https://developer.android.com/guide/navigation/use-graph/safe-args?hl=zh-tw)
- [AndroidDeveloper--在目的地之間傳遞資料](https://developer.android.com/guide/navigation/use-graph/pass-data?hl=zh-tw)
- [AndroidX — 如何使用Navigation的 Safe Args更安全的傳遞兩個Fragment的參數](https://jefflin1982.medium.com/androidx-%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8navigation%E7%9A%84-safe-args%E6%9B%B4%E5%AE%89%E5%85%A8%E7%9A%84%E5%82%B3%E9%81%9E%E5%85%A9%E5%80%8Bfragment%E7%9A%84%E5%8F%83%E6%95%B8-4e6bbab05472)
- [[Day12] Android - Kotlin筆記:JetPack - Fragments在Navigation中的參數傳遞(Safe Args)](https://ithelp.ithome.com.tw/articles/10264464)