前言
Navigation 直接翻譯即爲導航,它是 Android Jetpack 組件之一,讓單 Activity 應用成爲首選架構。應用內Fragment頁面的跳轉則由 Navigation 來處理,開發者無需在處理 FragmentTransaction 的複雜性以及相關的轉場動畫。
具體使用
在app的gradle.build中添加依賴:
def nav_version = "2.1.0"implementation "androidx.navigation:navigdef nav_version = "2.1.0"
implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
首先我們定義三個Fragment,分別爲Fragment1,Fragment2和Fragment3。實現邏輯爲Fragment1點擊跳轉到Fragment2,Fragment2點擊跳轉到Fragment3,Fragment3跳轉到Fragment1同時點擊返回鍵時也返回到Fragment1。
** navigation: 導航視圖XML的根結點。裏面定義相關fragment的跳轉邏輯。**
首先需要在res資源目錄下新建 navigation 文件夾,右鍵新建一個Navigation resource file命名爲nav_graph_main.xml。
文件左下腳分爲兩個Tab:Design和Text。Design視圖是可視化的,可以直接選擇相關fragment。Text視圖是我們手寫相關配置。
我們看下定義的nav_graph_main.xml文件:
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
app:startDestination="@id/fragment1">
<fragment
android:id="@+id/fragment1"
android:name="com.jetpack.jetpackdemo.navigation.fragment.Fragment1"
android:label="Fragment1"
tools:layout="@layout/fragment1_layout">
<action
android:id="@+id/fragment1_action"
app:destination="@+id/fragment2" />
</fragment>
<fragment
android:id="@+id/fragment2"
android:name="com.jetpack.jetpackdemo.navigation.fragment.Fragment2"
android:label="Fragment2"
tools:layout="@layout/fragment2_layout">
<action
android:id="@+id/fragment2_action"
app:destination="@+id/fragment3" />
</fragment>
<fragment
android:id="@+id/fragment3"
android:name="com.jetpack.jetpackdemo.navigation.fragment.Fragment3"
android:label="Fragment3"
tools:layout="@layout/fragment3_layout">
<action
android:id="@+id/fragment3_action"
app:popUpTo="@id/fragment1" />
</fragment>
</navigation>
navigation根節點中有個startDestination字段,他表示的是默認展示的是哪一個頁面。通過fragment標籤來定義要路由的相關頁面。id爲fragment唯一標識。name爲包名,必須保證正確。layout爲fragment的佈局文件,配置後方便在Design視圖中查看。
fragment中配置了子節點 action 。action表示的就是具體要路由的行爲。同樣id也是其唯一標識,destination表示的是目的地,即需要路由到具體的某一個頁面。popUpTo表示彈出到某一個頁面。action還有其他的屬性比如配置動畫等,具體請看Demo。
** NavHostFragment是導航視圖的展示容器。**
<fragment
android:id="@+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="0dp"
android:layout_height="0dp"
app:defaultNavHost="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:navGraph="@navigation/nav_graph_main" />
name爲固定寫法,必須指明爲
androidx.navigation.fragment.NavHostFragment
defaultNavHost字段表示是否攔截返回按鍵操作。
若爲true,需要的Activity中重寫onSupportNavigateUp方法。
因爲默認情況下返回鍵是不會回退fragment頁面的。
override fun onSupportNavigateUp(): Boolean {
return findNavController(R.id.nav_host_fragment).navigateUp()
}
navGraph字段即爲我們配置的navigation導航視圖。
NavController
通過findNavController來獲取NavController,通過controller的navigate或者navigateUp進行頁面之間的路由操作。
那麼在三個頁面的點擊按鈕的邏輯就是挑戰相應的頁面:
mBtn.setOnClickListener {
Navigation.findNavController(it).navigate(R.id.fragment1_action)
}
通過指定action的id來告訴Navigation跳轉的邏輯。其他頁面也是一樣。
最終效果:
我們來總結下 navigation、NavHostFragment以及NavController之間的關係。
navigation就是規劃了很多的路線,而這些路線需要在NavHostFragment中才能進行展示。展示後這麼多的路線該怎麼走呢,決定權就在NavController手中了,就像是方向盤一樣,控制着該走哪一個路線。
傳遞參數
在上文中我們講解了navigation相關的知識,其中還有一個子標籤:argument。是用來定義參數的。比如我們在fragment2標籤中添加argument標籤如下:
<argument
android:name="name"
android:defaultValue="navigation導航"
app:argType="string"
app:nullable="false" />
那麼在fragment1跳轉到fragment2的時候就可以攜帶參數了。其中 name 表示參數名稱。defaultValue即爲默認值。argType爲參數的類型。nullable表示是否可以爲空。
fragment之間傳遞參數有兩種方式:
- 傳統的Bundle方式
- 通過谷歌提供的safeArgs
傳統的Bundle方式
通過Bundle來設置和獲取參數。
在fragment1中進行設置:
mBtn.setOnClickListener {
//如果要使用 xml中argument的默認值則直接new Bundle() 傳入即可
val args = Bundle()
args.putString("name","通過bundle傳遞參數")
Navigation.findNavController(it).navigate(R.id.fragment1_action, args)
}
在fragment2中進行獲取參數:
val args = arguments
val name = args?.getString("name")
mTvName.text = name
這樣就可以將參數進行傳遞了。上面這種方式大家有沒有覺得有什麼問題呢?
參數名稱 “name” 我們在三處進行的手動填寫。這樣會很容易導致拼寫錯誤以及修改的時候容易漏改。很不友好。所以谷歌給我們提供了一個插件:safeArgs。下面我們來看下具體使用。
safeArgs
首先需要進行配置,在項目的 build.gradle 中添加classpath配置:
dependencies {
classpath 'android.arch.navigation:navigation-safe-args-gradle-plugin:1.0.0'
}
再在app的 build.gradle添加 apply plugin。
apply plugin: 'androidx.navigation.safeargs'
項目重新構建後會知道爲fragment生成後綴爲 Directions的文件。併爲navigation中有 argument 標籤的fragment自動生成後綴爲Args的文件。
通過後綴爲 Directions的文件進行參數的設置。後綴爲Args的文件進行參數的獲取。
fragment1中進行設置:
mBtn.setOnClickListener {
val args = Fragment1Directions.fragment1Action().setName("通過safeArgs進行參數傳遞")
Navigation.findNavController(it).navigate(R.id.fragment1_action, args.arguments)
}
fragment2中進行獲取:
val name = Fragment2Args.fromBundle(arguments!!).name
mTvName.text = name
這樣就完成了fragment之間參數的傳遞。完全避免了手動設置參數的邏輯。直接通過setter和getter進行參數的操作。
總結
總體來說Navigation的使用並不複雜,它讓我們單Activity架構成爲可能,無需關心具體的fragment的跳轉邏輯。但是同樣也是有問題的,通過源碼分析我們知道
在NavHostFragment的onCreateView中是創建了FrameLayout,也就是說其實真正的容器是FrameLayout。在創建FragmentNavigator的時候內部使用的是replace這個API,而不是show和hide。這就會導致fragment每次生命週期都會重新執行。所以和ViewModel結合使用效果應該更好。
本文在開源項目:https://github.com/xieyuliang/Note-Android中已收錄,裏面包含不同方向的自學編程路線、面試題集合/面經、及系列技術文章等,資源持續更新中……
這次就分享到這裏吧,下篇見。
原文鏈接:https://blog.csdn.net/m0_46962786/article/details/109119746