目录
[1. Navigation 简介与背景](#1. Navigation 简介与背景)
[1.1 为什么需要 Navigation?](#1.1 为什么需要 Navigation?)
[1.2 Navigation 的优势](#1.2 Navigation 的优势)
[2. 核心概念与架构](#2. 核心概念与架构)
[2.1 核心组件](#2.1 核心组件)
[2.2 导航类型](#2.2 导航类型)
[3. 基础使用与实践](#3. 基础使用与实践)
[3.1 项目配置](#3.1 项目配置)
[3.2 基本导航操作](#3.2 基本导航操作)
[3.3 参数传递详解](#3.3 参数传递详解)
[4. 高级特性](#4. 高级特性)
[4.1 返回栈管理](#4.1 返回栈管理)
[4.2 深层链接(Deep Link)](#4.2 深层链接(Deep Link))
[4.3 底部导航栏集成](#4.3 底部导航栏集成)
[5. 实际项目应用](#5. 实际项目应用)
[5.1 单 Activity + 多 Fragment 架构](#5.1 单 Activity + 多 Fragment 架构)
[5.2 多模块化项目](#5.2 多模块化项目)
[5.3 动态导航](#5.3 动态导航)
[6. 最佳实践与注意事项](#6. 最佳实践与注意事项)
[7. 常见问题与解决方案](#7. 常见问题与解决方案)
[7.1 Fragment 重复创建](#7.1 Fragment 重复创建)
[7.2 参数丢失](#7.2 参数丢失)
[7.3 返回栈异常](#7.3 返回栈异常)
[7.4 多 NavHost 场景](#7.4 多 NavHost 场景)
[8. 总结](#8. 总结)
上一篇:
Android Jetpack 组件库 ->Jetpack Navigation (上)_android jetpack navigation-CSDN博客
1. Navigation 简介与背景
1.1 为什么需要 Navigation?
传统导航的问题:
Kotlin
// 传统方式 - 手动管理 Fragment
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 手动添加 Fragment
supportFragmentManager.beginTransaction()
.add(R.id.container, HomeFragment())
.commit()
}
fun navigateToDetail(itemId: String) {
// 手动处理导航逻辑
val fragment = DetailFragment.newInstance(itemId)
supportFragmentManager.beginTransaction()
.replace(R.id.container, fragment)
.addToBackStack(null)
.commit()
}
}
问题:
- 代码重复且容易出错
- 难以管理返回栈
- 参数传递不安全
- 难以处理深层链接
- 测试困难
1.2 Navigation 的优势
Kotlin
// Navigation 方式 - 声明式导航
// 在导航图中定义
<action
android:id="@+id/action_home_to_detail"
app:destination="@id/detailFragment" />
// 在代码中使用
findNavController().navigate(R.id.action_home_to_detail)
优势:
- ✅ 可视化编辑导航图
- ✅ 类型安全的参数传递
- ✅ 自动管理返回栈
- ✅ 支持深层链接
- ✅ 易于测试
- ✅ 支持动画和转场
2. 核心概念与架构
2.1 核心组件
xml
Apply
<!-- Navigation 架构图 -->
Navigation Graph (导航图)
↓
NavController (导航控制器)
↓
NavHost (导航容器)
↓
NavDestination (导航目标)
详细说明:
- Navigation Graph(导航图)
- 定义应用的所有导航路径
- 可视化编辑界面
- XML 格式存储
- NavController(导航控制器)
- 管理导航操作
- 处理返回栈
- 执行导航动作
- NavHost(导航容器)
- 显示当前目标
- FragmentContainerView 实现
- 管理 Fragment 生命周期
- NavDestination(导航目标)
- 具体的导航目标(Fragment/Activity)
- 包含参数定义
- 支持深层链接
2.2 导航类型
<!-- 三种导航类型 -->
<fragment android:id="@+id/fragment_dest" />
<activity android:id="@+id/activity_dest" />
<navigation android:id="@+id/nested_nav" />
3. 基础使用与实践
3.1 项目配置
步骤1:添加依赖
Groovy
// build.gradle (app)
dependencies {
def nav_version = "2.7.5"
// Navigation Fragment
implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
// Navigation Testing
androidTestImplementation "androidx.navigation:navigation-testing:$nav_version"
}
// build.gradle (project)
plugins {
id "androidx.navigation.safeargs.kotlin" version "2.7.5"
}
步骤2:创建导航图
XML
<!-- res/navigation/nav_graph.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"
android:id="@+id/nav_graph"
app:startDestination="@id/homeFragment">
<!-- 首页 -->
<fragment
android:id="@+id/homeFragment"
android:name="com.example.app.HomeFragment"
android:label="首页">
<action
android:id="@+id/action_home_to_detail"
app:destination="@id/detailFragment"
app:enterAnim="@anim/slide_in_right"
app:exitAnim="@anim/slide_out_left" />
<action
android:id="@+id/action_home_to_profile"
app:destination="@id/profileFragment" />
</fragment>
<!-- 详情页 -->
<fragment
android:id="@+id/detailFragment"
android:name="com.example.app.DetailFragment"
android:label="详情">
<argument
android:name="itemId"
app:argType="string"
app:nullable="false"
app:defaultValue="0" />
<argument
android:name="itemName"
app:argType="string"
app:nullable="true" />
<argument
android:name="itemPrice"
app:argType="float"
app:nullable="true" />
</fragment>
<!-- 个人资料页 -->
<fragment
android:id="@+id/profileFragment"
android:name="com.example.app.ProfileFragment"
android:label="个人资料" />
</navigation>
步骤3:设置 NavHost
Kotlin
<!-- activity_main.xml -->
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.fragment.app.FragmentContainerView
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_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:navGraph="@navigation/nav_graph" />
</androidx.constraintlayout.widget.ConstraintLayout>
3.2 基本导航操作
获取 NavController:
Kotlin
// 在 Fragment 中
class HomeFragment : Fragment() {
private lateinit var navController: NavController
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// 方法1:通过 findNavController()
navController = findNavController()
// 方法2:通过 NavHostFragment
navController = NavHostFragment.findNavController(this)
// 方法3:通过 Activity
navController = (activity as MainActivity).findNavController(R.id.nav_host_fragment)
}
}
基本导航:
Kotlin
// 简单导航
navController.navigate(R.id.detailFragment)
// 带参数导航
val bundle = Bundle().apply {
putString("itemId", "123")
putString("itemName", "测试商品")
}
navController.navigate(R.id.detailFragment, bundle)
// 使用 Safe Args(推荐)
val action = HomeFragmentDirections.actionHomeToDetail(
itemId = "123",
itemName = "测试商品",
itemPrice = 99.99f
)
navController.navigate(action)
3.3 参数传递详解
参数类型:
<!-- 支持的数据类型 -->
<argument android:name="stringArg" app:argType="string" />
<argument android:name="intArg" app:argType="integer" />
<argument android:name="floatArg" app:argType="float" />
<argument android:name="boolArg" app:argType="boolean" />
<argument android:name="longArg" app:argType="long" />
<argument android:name="arrayArg" app:argType="string[]" />
<argument android:name="parcelableArg" app:argType="com.example.User" />
<argument android:name="enumArg" app:argType="com.example.UserType" />
接收参数:
Kotlin
class DetailFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// 方法1:通过 arguments
val itemId = arguments?.getString("itemId")
val itemName = arguments?.getString("itemName")
// 方法2:使用 Safe Args(推荐)
val args = DetailFragmentArgs.fromBundle(arguments!!)
val itemId = args.itemId
val itemName = args.itemName
val itemPrice = args.itemPrice
// 使用参数
updateUI(itemId, itemName, itemPrice)
}
private fun updateUI(itemId: String, itemName: String?, itemPrice: Float?) {
// 更新界面
}
}
4. 高级特性
4.1 返回栈管理
Kotlin
// 基本返回
navController.popBackStack()
// 返回到指定目标
navController.popBackStack(R.id.homeFragment, false)
// 清除返回栈并导航
navController.navigate(R.id.detailFragment) {
popUpTo(R.id.homeFragment) { inclusive = true }
}
// 设置返回栈行为
<action
android:id="@+id/action_home_to_detail"
app:destination="@id/detailFragment"
app:popUpTo="@id/homeFragment"
app:popUpToInclusive="false" />
4.2 深层链接(Deep Link)
应用内深层链接:
Kotlin
<fragment
android:id="@+id/detailFragment"
android:name="com.example.app.DetailFragment">
<deepLink
android:id="@+id/deepLink"
app:uri="myapp://detail/{itemId}" />
<argument
android:name="itemId"
app:argType="string" />
</fragment>
Web 深层链接:
Kotlin
<fragment
android:id="@+id/detailFragment"
android:name="com.example.app.DetailFragment">
<deepLink
app:uri="https://myapp.com/detail/{itemId}" />
<argument
android:name="itemId"
app:argType="string" />
</fragment>
处理深层链接:
Kotlin
// 在 Activity 中处理
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val navController = findNavController(R.id.nav_host_fragment)
// 处理深层链接
navController.handleDeepLink(intent)
}
}
4.3 底部导航栏集成
创建底部导航菜单:
Kotlin
<!-- res/menu/bottom_nav_menu.xml -->
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/homeFragment"
android:icon="@drawable/ic_home"
android:title="首页" />
<item
android:id="@+id/categoryFragment"
android:icon="@drawable/ic_category"
android:title="分类" />
<item
android:id="@+id/profileFragment"
android:icon="@drawable/ic_profile"
android:title="我的" />
</menu>
集成底部导航:
XML
<!-- activity_main.xml -->
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.fragment.app.FragmentContainerView
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_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@id/bottom_navigation"
app:navGraph="@navigation/nav_graph" />
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottom_navigation"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:menu="@menu/bottom_nav_menu" />
</androidx.constraintlayout.widget.ConstraintLayout>
在 Activity 中设置:
Kotlin
// 监听导航变化
navController.addOnDestinationChangedListener { _, destination, _ ->
when (destination.id) {
R.id.homeFragment -> {
// 进入首页
}
R.id.categoryFragment -> {
// 进入分类页
}
R.id.profileFragment -> {
// 进入个人资料页
}
}
}
}
}
5. 实际项目应用
5.1 单 Activity + 多 Fragment 架构
- Navigation 最适合单 Activity 架构,所有页面用 Fragment 实现,统一由 NavController 管理。
- 例如:主界面、详情页、个人中心等都作为 Fragment,Activity 只负责承载 NavHost。
5.2 多模块化项目
- Navigation 支持 navigation graph 嵌套,可以将不同业务模块的导航图拆分,主导航图通过 <include> 标签引入子导航图,便于团队协作和模块解耦。
Kotlin
<!-- 主导航图 -->
<navigation ...>
<include app:graph="@navigation/feature_a_nav" />
<include app:graph="@navigation/feature_b_nav" />
</navigation>
5.3 动态导航
- 可以根据业务逻辑动态决定导航目标,例如登录后跳转到不同页面。
Kotlin
if (user.isLoggedIn) {
navController.navigate(R.id.action_to_home)
} else {
navController.navigate(R.id.action_to_login)
}
6. 最佳实践与注意事项
- 优先使用 Safe Args
- 避免 Bundle 传参出错,提升类型安全。
- 合理设计导航图结构
- 避免过深嵌套,保持导航图清晰。
- 处理返回栈
- 善用 popUpTo 和 inclusive,避免页面堆积。
- 深层链接支持
- 方便外部唤起和 App 内跳转。
- 动画与转场
- 提升用户体验,使用自定义动画资源。
- 测试导航逻辑
- 使用 Navigation Testing 库进行单元测试和 UI 测试。
7. 常见问题与解决方案
7.1 Fragment 重复创建
- 避免在导航时重复 navigate 到同一个目标,可以先判断当前 destination。
7.2 参数丢失
- 使用 Safe Args,避免 Bundle 传参遗漏或类型错误。
7.3 返回栈异常
- 检查导航图中 popUpTo 配置,确保返回栈符合预期。
7.4 多 NavHost 场景
- 多 NavHost 时要分别管理各自的 NavController,避免混淆。
8. 总结
Jetpack Navigation 组件极大简化了 Android 应用的导航开发,提升了代码的可维护性和安全性。
- 推荐在新项目中优先采用 Navigation 组件,配合单 Activity 架构和 Safe Args 使用。
- 合理设计导航图,充分利用深层链接、动画、返回栈等高级特性,打造高质量的用户体验。