前言
Google 在2018年推出了 Android Jetpack,在Jetpack里有一種管理fragment的新架構模式,那就是navigation. 字面意思是導航,但是除了做APP引導頁面以外.也可以使用在App主頁分tab的情況.. 甚至可以一個功能模塊就一個activity大部分頁面UI都使用fragment來實現,而navigation就成了管理fragment至關重要的架構.
但是,它不單單只能管理fragment也可以管理activity.這點你格外注意.
使用條件
你的Android studio 必需升級到3.2版本以上,此博客正在寫的時候Android studio已經到達4.2.1,所以升級到最新版本即可.
依賴
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.0' implementation 'androidx.navigation:navigation-ui-ktx:2.3.0'
Android studio的4.2.1版本默認是添加這兩個依賴的
使用流程
創建navigation目錄
1.選中項目資源文件夾 res 右擊 >> New >> New Resource Directory(或 Android Resource Directory)
2.選中navigation 點擊創建 (注意這個目錄只有在Android studio3.2版本以上才能出現)
3、選擇Resource type 為 navigation, 點擊OK

點擊OK

然后選中navigation文件夾目錄,創建navigation目錄下的xml文件
1.選中項目資源文件夾 res 右擊 >> New >> New Resource File(或Navigation Resource File)
2.選擇navigation ,輸入xml文件名稱,點擊ok創建

配置創建的xml文件
上面我們創建了一個叫demo_nav.xml的navigation文件,現在我們需要來設配它來管理fragment
1.打開這個文件選
模式使用視圖手動配置.
2.切換到Design模式后,我們可以看到下面這個界面(恩,一片空白). 我們可以在左上角點擊
添加圖標,進入添加內容的操作.
3.點擊
后,可以看到下面這個彈窗,這里解釋一下:
第一個 Create new destinattion,字面意思創建一個新目標(其實就是創建fragment,當然你也可以手動另外創建fragment不一定需要在這里創建)
第二個 placeholder,這個就是重點了. 這是一個管理fragment跳轉的節點,我們點擊后可以創建它.為了了解它的使用方式,點擊3次創建三個節點

4.節點創建后可以看到三個節點(看下面圖片,這些節點都是我已經導入fragment了.不要急后面會講解如何導入).這里有一個重點! 你可以點擊這些頁面(會有一個藍點),點擊藍點按住向右分配它需要跳轉的另外一個頁面.(它會自動生成一些我們跳轉的代碼)

6.然后點擊左下角的Text模式,在Text模式下,可以看到如下代碼,在上面的圖片中你可以很清楚的看到創建了3個節點,並且是一個跳轉一個的.從第一個fragment跳轉到第二個fragment,再從第二個fragment跳轉到第三個fragment
下面我們來重點講解下下面的這些代碼的關鍵點了:
在<navigation里的屬性:
1.android:id="@+id/demo_nav" 這個屬性是你這個xml文件navigation的id,很重要,我們需要在activity的xml布局里引用,記得寫上不要忘記
2.app:startDestination="@id/homeFragment" 這個屬性是你首次加載的第一個頁面,很重要,一般就是第一個fragment
在<fragment 里的屬性:
其實就是一個節點你也可以理解成一個fragment
1.android:id="@+id/homeFragment" 每一個fragment節點都需要有自己的id,很重要. 我們需要在后面的節點上使用這些id指定跳轉目標
2.android:name="com.example.jgdemo.fragments.HomeFragment" 這個屬性是你這個節點所對應的fragment(需要你導入指定的fragment文件路徑),這個很重要
3.android:label="HomeFragment" 一個標簽名稱,用於記錄這個節點的標簽信息(大概可能是在代碼里的Intent里獲取來知曉此次是那個fragment節點在跳轉,沒深究了)
4.tools:layout="@layout/fragment_home" 這個屬性不是重要的,設置它后你可以在切換到Design模式后看到,視圖頁面的fragment的預覽圖(就在上面的圖片里,可以直接看到fragment效果)
在<action 里的屬性:
action 負責編寫跳轉動作
1. android:id="@+id/action_homeFragment_to_myOneFragment" 這個很重要,它是這個跳轉動作的id, 這個id我們將在后面的代碼中調用,用於執行fragment的跳轉
2. app:destination="@id/myOneFragment" 跳轉的目標fragment,這個很重要
3、
app:enterAnim="@anim/nav_default_enter_anim"
app:exitAnim="@anim/nav_default_exit_anim"
app:popEnterAnim="@anim/nav_default_pop_enter_anim"
app:popExitAnim="@anim/nav_default_pop_exit_anim"
是使用的系統自帶的動畫
整體如下:
<?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/demo_nav"
app:startDestination="@id/homeFragment"><!--首次加載的第一個頁面-->
<fragment
android:id="@+id/homeFragment"
android:name="com.example.jgdemo.fragments.HomeFragment"
android:label="HomeFragment"
tools:layout="@layout/fragment_home">
<action
android:id="@+id/action_homeFragment_to_myOneFragment"
app:destination="@id/myOneFragment"
app:enterAnim="@anim/nav_default_enter_anim"
app:exitAnim="@anim/nav_default_exit_anim"
app:popEnterAnim="@anim/nav_default_pop_enter_anim"
app:popExitAnim="@anim/nav_default_pop_exit_anim" />
<argument
android:name="userName"
app:argType="string"
app:nullable="true"
android:defaultValue="unknown" />
<argument
android:name="age"
app:argType="integer"
android:defaultValue="0" />
</fragment>
<fragment
android:id="@+id/myOneFragment"
android:name="com.example.jgdemo.fragments.MyOneFragment"
android:label="MyOneFragment"
tools:layout="@layout/fragment_myone">
<action
android:id="@+id/action_myOneFragment_to_myTwoFragment"
app:destination="@id/myTwoFragment" />
<action
android:id="@+id/action_myOneFragment_to_myThreeFragment"
app:destination="@id/myThreeFragment" />
<argument
android:name="myname"
app:argType="string"
app:nullable="true"
android:defaultValue="unknown" />
<argument
android:name="myage"
app:argType="integer"
android:defaultValue="0" />
</fragment>
<fragment
android:id="@+id/myTwoFragment"
android:name="com.example.jgdemo.fragments.MyTwoFragment"
android:label="MyTwoFragment"
tools:layout="@layout/fragment_mytwo"/>
<fragment
android:id="@+id/myThreeFragment"
android:name="com.example.jgdemo.fragments.MyThreeFragment"
android:label="MyThreeFragment"
tools:layout="@layout/fragment_mythree"/>
</navigation>
讓navigation與Activity關聯起來
現在我們已經創建了navigation,但是使用它還需要一個根Activity,它畢竟還是需要依托Activity的.
1.創建了一個叫DemoActivity的Activity.這個沒啥,下面來看這個Activity的布局xml怎么配(如下xml代碼)
我們就關注fragment的一些屬性
1.android:name="androidx.navigation.fragment.NavHostFragment" 這個非常重要,這是你告知fragment需要使用navigation模式的關鍵屬性,另外它是固定死的.你必需寫.
2. app:defaultNavHost="true" 這是你實現物理按鍵(比如返回鍵),是按一下退出一個fragment 還是直接退出這個Activity的關鍵屬性
3.app:navGraph="@navigation/demo_nav" 很重要,這就是我們前面創建的navigation的xml文件
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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容器-->
<androidx.fragment.app.FragmentContainerView
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/demo_nav"></androidx.fragment.app.FragmentContainerView>
</FrameLayout>
實現fragment跳轉與返回
進入到MainActivity后,首先會自動加載到第一個fragment. 然后我們看看如何跳轉到其他fragment中
1.從第一個碎片跳轉到第二個碎片,關鍵代碼 Navigation.findNavController(binding.root).navigate(R.id.action_homeFragment_to_myOneFragment)
class HomeFragment : Fragment(){
//使用viewBinding直接省去findViewById
private lateinit var binding: FragmentHomeBinding
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = FragmentHomeBinding.inflate(layoutInflater)
binding.jumptomyoneBtn.setOnClickListener {
//跳轉到MyOneFragment
Navigation.findNavController(binding.root).navigate(R.id.action_homeFragment_to_myOneFragment)
}
return binding.root
}
}
2.從第二個碎片返回到第一個碎片,關鍵代碼 Navigation.findNavController(binding.root).popBackStack()
class MyOneFragment : Fragment(){
//使用viewBinding直接省去findViewById
private lateinit var binding: FragmentMyoneBinding
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = FragmentMyoneBinding.inflate(layoutInflater)
//返回
binding.closemyoneBtn.setOnClickListener {
//關閉頁面
Navigation.findNavController(binding.root).popBackStack()
}
return binding.root
}
}
上面我使用了谷歌的ViewBinding直接省去findViewById
用法:在build.gradle的 android 閉包中添加
buildFeatures {
viewBinding true
}
實現fragment傳值
打開你的nvagation文件,選中Design
選中你的Fragment視圖

然后右側選中Arguments,點擊加號,添加你要傳遞的參數

然后會自動生成代碼
<argument
android:name="userName"
app:argType="string"
app:nullable="true"
android:defaultValue="unknown" />
<argument
android:name="age"
app:argType="integer"
android:defaultValue="0" />
傳遞參數
val bundle = HomeFragmentArgs("張三",24).toBundle()
Navigation.findNavController(binding.root).navigate(R.id.action_homeFragment_to_myOneFragment,bundle)
接收參數
// 接收傳遞來的參數 方法1
val bundle = arguments
val username = bundle?.getString("userName")
val age = bundle?.getInt("age",0)
Log.i("打印接收傳遞來的數據:","用戶名:$username ,年齡: ${age.toString()}")
接收傳遞來的參數 方法2
val bundle = arguments
val username = arguments?.let { HomeFragmentArgs.fromBundle(it).userName }
val age = arguments?.let { HomeFragmentArgs.fromBundle(it).age }
Log.i("打印接收傳遞來的數據:","用戶名:$username ,年齡: ${age.toString()}")
使用了 safe args插件傳遞參數,配置如下
在Project的build.gradle文件中添加safe args插件
def nav_version = "2.3.0-alpha01"
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version"
如下:
buildscript {
ext.kotlin_version = "1.5.0"
//
ext.nav_version = "2.3.0-alpha01"
repositories {
google()
mavenCentral()
jcenter()
}
dependencies {
classpath "com.android.tools.build:gradle:4.2.1"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
//
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version"
}
}
allprojects {
repositories {
google()
mavenCentral()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
然后需要引用這個插件。在app的build.gradle中添加依賴。
如果需要java或jave和kotlin的,添加這個:
id 'androidx.navigation.safeargs'
純kotlin的,添加這個:
id 'androidx.navigation.safeargs.kotlin'
如圖:

最后上一下我完整代碼:
demo_nav.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"
android:id="@+id/demo_nav"
app:startDestination="@id/homeFragment"><!--首次加載的第一個頁面-->
<fragment
android:id="@+id/homeFragment"
android:name="com.example.jgdemo.fragments.HomeFragment"
android:label="HomeFragment"
tools:layout="@layout/fragment_home">
<action
android:id="@+id/action_homeFragment_to_myOneFragment"
app:destination="@id/myOneFragment"
app:enterAnim="@anim/nav_default_enter_anim"
app:exitAnim="@anim/nav_default_exit_anim"
app:popEnterAnim="@anim/nav_default_pop_enter_anim"
app:popExitAnim="@anim/nav_default_pop_exit_anim" />
<argument
android:name="userName"
app:argType="string"
app:nullable="true"
android:defaultValue="unknown" />
<argument
android:name="age"
app:argType="integer"
android:defaultValue="0" />
</fragment>
<fragment
android:id="@+id/myOneFragment"
android:name="com.example.jgdemo.fragments.MyOneFragment"
android:label="MyOneFragment"
tools:layout="@layout/fragment_myone">
<action
android:id="@+id/action_myOneFragment_to_myTwoFragment"
app:destination="@id/myTwoFragment" />
<action
android:id="@+id/action_myOneFragment_to_myThreeFragment"
app:destination="@id/myThreeFragment" />
<argument
android:name="myname"
app:argType="string"
app:nullable="true"
android:defaultValue="unknown" />
<argument
android:name="myage"
app:argType="integer"
android:defaultValue="0" />
</fragment>
<fragment
android:id="@+id/myTwoFragment"
android:name="com.example.jgdemo.fragments.MyTwoFragment"
android:label="MyTwoFragment"
tools:layout="@layout/fragment_mytwo"/>
<fragment
android:id="@+id/myThreeFragment"
android:name="com.example.jgdemo.fragments.MyThreeFragment"
android:label="MyThreeFragment"
tools:layout="@layout/fragment_mythree"/>
</navigation>
HomeFragment
class HomeFragment : Fragment(){
private lateinit var binding: FragmentHomeBinding
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = FragmentHomeBinding.inflate(layoutInflater)
binding.jumptomyoneBtn.setOnClickListener {
//方法1、使用Directions
//不傳遞參數
//var action = HomeFragmentDirections.actionHomeFragmentToMyOneFragment()
//findNavController().navigate(action)
//傳遞參數,需要接收參數的fragment中寫argument
var action = HomeFragmentDirections.actionHomeFragmentToMyOneFragment("這是我的名字",24)//參數直接寫入
findNavController().navigate(action)
//方法2、直接使用Id
//不傳遞參數
//Navigation.findNavController(binding.root).navigate(R.id.action_homeFragment_to_myOneFragment)
//傳遞參數1 對應 接收參數1和2
//val bundle = HomeFragmentArgs("張三",24).toBundle()
//Navigation.findNavController(binding.root).navigate(R.id.action_homeFragment_to_myOneFragment,bundle)
}
return binding.root
}
}
MyOneFragment
class MyOneFragment : Fragment(){
private lateinit var binding: FragmentMyoneBinding
private val args: MyOneFragmentArgs by navArgs()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = FragmentMyoneBinding.inflate(layoutInflater)
//返回
binding.closemyoneBtn.setOnClickListener {
//方法1、使用Directions
//findNavController().popBackStack()
//方法2、
Navigation.findNavController(binding.root).popBackStack()
}
//跳轉到第三個頁面
binding.jumptomytwoBtn.setOnClickListener {
//方法1、使用Directions
//val action = MyOneFragmentDirections.actionMyOneFragmentToMyTwoFragment()
//findNavController().navigate(action)
//方法2、直接使用Id
Navigation.findNavController(binding.root).navigate(R.id.action_myOneFragment_to_myTwoFragment)
}
//接收傳遞來的參數 方法1
//val bundle = arguments
//val username = bundle?.getString("userName")
//val age = bundle?.getInt("age",0)
//Log.i("打印接收傳遞來的數據:","用戶名:$username ,年齡: ${age.toString()}")
//接收傳遞來的參數 方法2
//val bundle = arguments
//val username = arguments?.let { HomeFragmentArgs.fromBundle(it).userName }
//val age = arguments?.let { HomeFragmentArgs.fromBundle(it).age }
//Log.i("打印接收傳遞來的數據:","用戶名:$username ,年齡: ${age.toString()}")
//接收傳遞來的參數
Log.i("打印接收的參數",args.myname +" ,"+ args.myage)
return binding.root
}
}
完成
參考:
https://www.cnblogs.com/guanxinjing/p/11555217.html
https://blog.csdn.net/nanquan11/article/details/109807501
https://www.it1352.com/1534512.html
