<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>&lt; <u>特點</u> &gt;</b></font> - <u>類型安全</u>: <code>SafeArgs</code> 生成的代碼會自動檢查參數的類型,避免了傳遞錯誤類型或因手動鍵入參數名稱錯誤而引發的崩潰 - <u>編譯時檢查</u>: <code>SafeArgs</code> 會在編譯時檢查導航操作和參數設置是否正確,減少因執行時錯誤造成的問題。 - <u>自動生成代碼</u>: 通過 <code>SafeArgs</code>,Android Studio 會自動生成導航操作和參數的代碼,大幅減少手動處理的工作量。 <font size=4><b>&lt; <u>工作原理</u> &gt;</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") } ``` ![image](https://hackmd.io/_uploads/ryJotqsG1l.png) </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 中點選加號後進行設定 ![image](https://hackmd.io/_uploads/ByOR9HhMye.png) 2. 設定完成後,Arguments 就會被附加到導航圖中的目標 Fragment 的設定之中 ![image](https://hackmd.io/_uploads/BJME2S2Gkg.png) >[!Important] >- 這個設定是多對一的,也就是說,所有「目的地為此 Fragment 的 起點 Fragment」,都必須要提交我們設定在目標 Fragment 上的 Argument 才能過來 >- 例如: 只要是以 FragmentB 為目標的 Fragment(例如: FragmentA、C、D),都必須提供一個 <code>String 參數</code> <code>nameBBB</code>,否則會出現錯誤 ![image](https://hackmd.io/_uploads/SkmOkU3G1g.png) </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) 使用 --- ![image](https://hackmd.io/_uploads/SkrKvU3Mkx.png) ```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> 中獲取需要的參數 --- ![image](https://hackmd.io/_uploads/S1jX7-azye.png) ```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)