背景:Google 在2018年推出的 Android Jetpack中有一種新架構(gòu)模式,那就是navigation,字面意思是導(dǎo)航。但是除了做APP引導(dǎo)頁面以外,它也可以使用在App主頁分tab的情況,甚至可以一個(gè)功能模塊就一個(gè)activity大部分頁面UI都使用fragment來實(shí)現(xiàn)。因此,navigation就成了管理fragment至關(guān)重要的架構(gòu)。
一、Navigation三大構(gòu)成
- Navigation Graph:Navigaion Graph(導(dǎo)航圖)是一個(gè) XML 文件,定義了應(yīng)用程序中所有可導(dǎo)航的目標(biāo)(如 Fragment、Activity 或自定義目標(biāo)),以及它們之間的導(dǎo)航關(guān)系。包含所有被管理的 Fragment,起始目標(biāo),換頁目標(biāo),返回目標(biāo)等。
- NavHostFragment:NavHostFragment 是一個(gè)托管 Fragment 的容器,用于管理應(yīng)用程序中的導(dǎo)航圖。(注:它本質(zhì)是一個(gè)特殊的Fragment)
<fragment
android:id="@+id/navHostFragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNaHost="true"
app:navGraph="@navigation/nav_graph_main" />
- NavController:NavController 是用于管理導(dǎo)航操作的控制器,它負(fù)責(zé)根據(jù)導(dǎo)航圖中定義的導(dǎo)航關(guān)系,自動(dòng)切換目標(biāo) Fragment,并將其添加到回退棧中,以便用戶可以通過返回按鈕回退到上一個(gè)目標(biāo)。通常是寫在點(diǎn)擊事件內(nèi)完成 Fragment 的切換。
button.setOnClickListener {
findNavController().navigate(R.id.action_Fragment1_to_Fragment2)
}
二、基本使用
- 添加依賴庫
implementation("androidx.navigation:navigation-fragment-ktx:2.5.1")
implementation("androidx.navigation:navigation-ui-ktx:2.5.1")
2.創(chuàng)建導(dǎo)航視圖
- 右鍵res,點(diǎn)擊New -> Android Resource Directory
- 在出現(xiàn)的面板第二行Resource type 下拉列表中選擇 Navigation,然后點(diǎn)擊 OK
- res目錄下會(huì)多出一個(gè)navigation的資源目錄,右鍵該目錄,點(diǎn)擊New -> Navigation Resource File,輸入需要新建的資源文件名,這里命名nav_graph,點(diǎn)擊ok,一個(gè)nav_graph.xml就創(chuàng)建好了。
- 配置graph及fragment之間的關(guān)系(如創(chuàng)建A/B/C三個(gè)fragment)
<?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"
android:id="@+id/nav_graph"
app:startDestination="@id/fragmentA">
<fragment
android:id="@+id/fragmentA"
android:name="com.example.testnavigation.FragmentA"
android:label="fragment_a"
tools:layout="@layout/fragment_a" >
<action
android:id="@+id/action_fragmentA_to_fragmentB2"
app:destination="@id/fragmentB" />
</fragment>
<fragment
android:id="@+id/fragmentB"
android:name="com.example.testnavigation.FragmentB"
android:label="fragment_b"
tools:layout="@layout/fragment_b" >
<action
android:id="@+id/action_fragmentB_to_fragmentC2"
app:destination="@id/fragmentC" />
</fragment>
<fragment
android:id="@+id/fragmentC"
android:name="com.example.testnavigation.FragmentC"
android:label="fragment_c"
tools:layout="@layout/fragment_c" />
</navigation>
- navigation是根標(biāo)簽,通過startDestination配置默認(rèn)啟動(dòng)的第一個(gè)頁面,這里配置的是FragmentA
- fragment標(biāo)簽代表一個(gè)fragment,其實(shí)這里不僅可以配置fragment,也可以配置activity,甚至還可以自定義
- action標(biāo)簽定義了頁面跳轉(zhuǎn)的行為,相當(dāng)于上圖中的每條線,destination定義跳轉(zhuǎn)的目標(biāo)頁,還可以定義跳轉(zhuǎn)時(shí)的動(dòng)畫等等(當(dāng)調(diào)用到 action_FragmentA_to_FragmentB2 這個(gè) action,會(huì)從 FragmentA -> FragmentB)
- 添加NaviHostFragment
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<fragment
android:id="@+id/fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
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" />
</androidx.constraintlayout.widget.ConstraintLayout>
- android:name指定NavHostFragment
- app:navGraph指定導(dǎo)航視圖,即建好的nav_graph.xml
- app:defaultNavHost=true意思是可以攔截系統(tǒng)的返回鍵,可以理解為默認(rèn)給fragment實(shí)現(xiàn)了返回鍵的功能,這樣在fragment的跳轉(zhuǎn)過程中,當(dāng)我們按返回鍵時(shí),就可以使得fragment跟activity一樣可以回到上一個(gè)頁面了
- 通過NaviController管理fragment之間的跳轉(zhuǎn)
- 例如從fragmentA跳轉(zhuǎn)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
button.setOnClickListener {
val navController = Navigation.findNavController(it)
navController.navigate(R.id.action_fragmentA_to_fragmentB2)
}
}
- 首先得到navController對(duì)象,然后調(diào)用它的navigate方法,傳入前面nav_graph中定義的action的id即可。
- 可以在nav_graph中調(diào)整pop屬性來更改出入棧順序