navigation是負(fù)責(zé)fragment之間切換的處理工具
有三個核心點:
- Navigation Graph (New XML Resource): 控制中心鲜戒,包含所有頁面及頁面間的關(guān)系
- NavHostFragment (Layout XML View):特殊的Fragment隆圆,是其他Fragment的“容器”,Navigation Graph中所有Fragmnet正是通過這個特殊的Fragment進(jìn)行展示的
- NavController (Kotlin / Java Object): 負(fù)責(zé)完成Navigation中具體的頁面切換工作
三者之間的關(guān)系:當(dāng)需要切換頁面時俭识,使用NavController對象,告訴它想要去的Navigation Graph中的哪個Fragment洞渔,NavController對象會將目的地的Fragment展示在NavHostFragment中套媚。
一、基本使用
1. 添加依賴
dependencies {
......
def nav_version = "2.3.5"
// Kotlin
implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
}
2. 創(chuàng)建Navigation Graph
res文件夾 -> New -> Android Resource File
3. 添加NavHostFragmnet
在activity_main.xml文件中添加fragment作為NavHostFragment磁椒,其中有三點需要注意:
- name必須是"androidx.navigation.fragment.NavHostFragment"
- defaultNavHost設(shè)置為"true"
- navGraph設(shè)置為"@navigation/nav_graph"
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="@navigation/nav_graph" />
</RelativeLayout>
4. 創(chuàng)建Destination
在nav_graph.xml頁面里堤瘤,依次點擊加號、 Create new destination創(chuàng)建一個Destination浆熔,首次在此創(chuàng)建一個MainFragment作為StartDestination
<?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/mainFragment">
<fragment
android:id="@+id/mainFragment"
android:name="com.example.mynavigationdemo.MainFragment"
android:label="fragment_main"
tools:layout="@layout/fragment_main" />
</navigation>
5. 頁面切換
在nav_graph.xml頁面里本辐,就像創(chuàng)建MainFragment一樣創(chuàng)建一個SecondFragment
<?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/mainFragment">
<fragment
android:id="@+id/mainFragment"
android:name="com.example.mynavigationdemo.MainFragment"
android:label="fragment_main"
tools:layout="@layout/fragment_main" />
<fragment
android:id="@+id/secondFragment"
android:name="com.example.mynavigationdemo.SecondFragment"
android:label="fragment_second"
tools:layout="@layout/fragment_second" />
</navigation>
5.1 創(chuàng)建Action
在nav_graph.xml頁面的Design面板里,單擊MainFragment,然后選中右側(cè)的圓圈熱點并拖拽指向右側(cè)的SecondFragmnt
<?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/mainFragment">
<fragment
android:id="@+id/mainFragment"
android:name="com.example.mynavigationdemo.MainFragment"
android:label="fragment_main"
tools:layout="@layout/fragment_main">
<action
android:id="@+id/action_mainFragment_to_secondFragment"
app:destination="@id/secondFragment" />
</fragment>
<fragment
android:id="@+id/secondFragment"
android:name="com.example.mynavigationdemo.SecondFragment"
android:label="fragment_second"
tools:layout="@layout/fragment_second" />
</navigation>
5.2 使用NavController導(dǎo)航到目的地
在fragment_main.xml中添加一個按鈕慎皱,然后在MainFragment文件中添加該按鈕點擊事件的監(jiān)聽器环葵,當(dāng)點擊該按鈕時通過NavController完成頁面跳轉(zhuǎn)
class MainFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
view.findViewById<Button>(R.id.btnToSecondFragment).setOnClickListener {
findNavController().navigate(R.id.action_mainFragment_to_secondFragment)
}
}
}
導(dǎo)航到目的地是使用 NavController 完成的,它是一個在 NavHost 中管理應(yīng)用導(dǎo)航的對象宝冕。每個 NavHost 均有自己的相應(yīng)NavController张遭,NavController 提供了幾種導(dǎo)航到目的地的不同方式,如需從 Fragment地梨、Activity 或View中獲取NavController菊卷,請使用以下某種方法:
- Fragment.findNavController()
- View.findNavController()
- Activity.findNavController(viewId: Int)
5.3 添加切換動畫
在nav_graph.xml頁面的Design面板里,點擊MainFragment指向SecondFragment的箭頭(即頁面跳轉(zhuǎn)Action)宝剖,然后在Attributes面板的Animations部分中洁闰,點擊要添加的動畫旁邊的下拉箭頭,選擇需要設(shè)置的動畫
<?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/mainFragment">
<fragment
android:id="@+id/mainFragment"
android:name="com.example.mynavigationdemo.MainFragment"
android:label="fragment_main"
tools:layout="@layout/fragment_main">
<action
android:id="@+id/action_mainFragment_to_secondFragment"
app:destination="@id/secondFragment"
app:enterAnim="@android:anim/fade_in"
app:exitAnim="@android:anim/fade_out"
app:popEnterAnim="@android:anim/fade_in"
app:popExitAnim="@android:anim/fade_out" />
</fragment>
<fragment
android:id="@+id/secondFragment"
android:name="com.example.mynavigationdemo.SecondFragment"
android:label="fragment_second"
tools:layout="@layout/fragment_second" />
</navigation>
6. 傳遞參數(shù)
6.1 基本類型參數(shù)
6.1.1 添加參數(shù)
在nav_graph.xml頁面的Design面板里万细,點擊接收參數(shù)的目的地Fragment(此處為secondFragment), 然后在Attributes面板的Arguments列點擊右側(cè)添加按鈕(+)可添加參數(shù)
<?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/mainFragment">
<fragment
android:id="@+id/mainFragment"
android:name="com.example.mynavigationdemo.MainFragment"
android:label="fragment_main"
tools:layout="@layout/fragment_main">
<action
android:id="@+id/action_mainFragment_to_secondFragment"
app:destination="@id/secondFragment"
app:enterAnim="@android:anim/fade_in"
app:exitAnim="@android:anim/fade_out"
app:popEnterAnim="@android:anim/fade_in"
app:popExitAnim="@android:anim/fade_out" />
</fragment>
<fragment
android:id="@+id/secondFragment"
android:name="com.example.mynavigationdemo.SecondFragment"
android:label="fragment_second"
tools:layout="@layout/fragment_second" >
<argument
android:name="name"
app:argType="string"
android:defaultValue='"HSG"' />
</fragment>
</navigation>
6.1.2 接收參數(shù)
class SecondFragment : Fragment() {
val TAG = "SecondFragment"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val name = arguments?.getString("name")
Log.d(TAG, "onCreate() called with: name = $name")
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_second, container, false)
}
}
logcat 輸出:D/SecondFragment: onCreate() called with: name = "HSG"
6.1.3 傳遞參數(shù)
class MainFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
view.findViewById<Button>(R.id.btnToSecondFragment).setOnClickListener {
val bundle = Bundle()
bundle.putString("name", "Hello World")
findNavController().navigate(R.id.action_mainFragment_to_secondFragment, bundle)
}
}
}
logcat 輸出:D/SecondFragment: onCreate() called with: name = Hello Word
6.2 使用safe args傳遞參數(shù)
該插件可以生成簡單的 object和builder類扑眉,以便以類型安全的方式瀏覽和訪問任何關(guān)聯(lián)的參數(shù)。我們強烈建使用Safe Args進(jìn)行數(shù)據(jù)傳遞赖钞,因為它可以確保類型安全腰素。
6.2.1 添加依賴
在project的build.gradle文件中添加safe args插件:
buildscript {
repositories {
google()
}
dependencies {
def nav_version = "2.3.5"
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version"
}
}
在app的build.gradle文件中添加:
(1)如需生成適用于 Java 模塊或 Java 和 Kotlin 混合模塊的 Java 語言代碼,請?zhí)砑樱?/p>
apply plugin: "androidx.navigation.safeargs"
(2)如需生成適用于僅 Kotlin 模塊的 Kotlin 語言代碼雪营,請?zhí)砑樱?/p>
apply plugin: "androidx.navigation.safeargs.kotlin"
根據(jù)遷移到 AndroidX 文檔弓千,gradle.properties 文件中必須具有 android.useAndroidX=true
啟用 Safe Args 后,生成的代碼會為每個Action(包含發(fā)送方和接收方)提供類型安全的類和方法:
- 為Action的每一個發(fā)送方創(chuàng)建一個類献起。該類的名稱是在發(fā)送方的名稱后面加上“Directions”洋访。例如,如果發(fā)送方是名為MainFragment的Fragment,則生成的類的名稱為 MainFragmentDirections,該類會為發(fā)送方中定義的每個Action提供一個方法迈套。
- 對于用于傳遞參數(shù)的每個Action,都會創(chuàng)建一個 inner 類诡宗,該類的名稱根據(jù)Action的名稱確定。例如,如果Action名稱為 action_mainFragment_to_secondFragment,則類名稱為 ActionMainFragmentToSecondFragment善镰,可以此關(guān)聯(lián)的 action 類來設(shè)置參數(shù)值妹萨。
- 為接收目方創(chuàng)建一個類年枕。該類的名稱是在接收方的名稱后面加上“Args”。例如乎完,如果接收方的 Fragment 的名稱為 SecondFragment熏兄,則生成的類的名稱為 SecondFragmentArgs。可以使用該類的 fromBundle() 方法獲取傳遞的參數(shù)摩桶。
6.2.2 傳遞參數(shù)
class MainFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
view.findViewById<Button>(R.id.btnToSecondFragment).setOnClickListener {
// val bundle = Bundle()
// bundle.putString("name", "Hello World")
// findNavController().navigate(R.id.action_mainFragment_to_secondFragment, bundle)
val direction = MainFragmentDirections.actionMainFragmentToSecondFragment("Hello Android")
findNavController().navigate(direction)
}
}
}
6.2.3 接收參數(shù)
class SecondFragment : Fragment() {
val TAG = "SecondFragment"
private val args by navArgs<SecondFragmentArgs>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// val name = arguments?.getString("name")
Log.d(TAG, "onCreate() called with: name = ${args.name}")
}
}
logcat 輸出:D/SecondFragment: onCreate() called with: name = Hello Android
7. 配置ActionBar
通過Navigation完成頁面跳轉(zhuǎn)已基本完成桥状,但和Activity跳轉(zhuǎn)相比仍有不足之處——當(dāng)前的ActionBar標(biāo)題不會跟隨Navigation頁面變化,而且也沒有用于返回的小圖標(biāo)硝清。因此辅斟,本節(jié)就來解決該問題,Navigation已提供了便捷的解決方案芦拿,只需配置NavController對ActionBar進(jìn)行控制士飒,即可解決該問題。
在MainActivity中蔗崎,通過四步即可完成配置:
第1步酵幕,在Activity上獲取NavController
navController = findNavController(R.id.nav_host_fragment)
第2步,配置AppBarConfiguration缓苛,以便NavController接管ActionBar后能正確進(jìn)行控制
appbarConfiguration = AppBarConfiguration.Builder(navController.graph).build()
第3步芳撒,配置NavController對ActionBar進(jìn)行控制
NavigationUI.setupActionBarWithNavController(this, navController, appbarConfiguration)
第4步,讓NavController接管返回事件
override fun onSupportNavigateUp(): Boolean {
return NavigationUI.navigateUp(
navController,
appbarConfiguration
) or super.onSupportNavigateUp()
}
完整代碼:
class MainActivity : AppCompatActivity() {
private lateinit var appbarConfiguration: AppBarConfiguration
private lateinit var navController: NavController
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
navController = findNavController(R.id.nav_host_fragment)
appbarConfiguration = AppBarConfiguration.Builder(navController.graph).build()
NavigationUI.setupActionBarWithNavController(this, navController, appbarConfiguration)
}
override fun onSupportNavigateUp(): Boolean {
return NavigationUI.navigateUp(
navController,
appbarConfiguration
) or super.onSupportNavigateUp()
}
}