Android Jetpack 组件库 ->Jetpack Navigation (下)
目录
2. 核心概念与架构
2.1 核心组件
2.2 导航类型
3. 基础使用与实践
3.1 项目配置
3.2 基本导航操作
3.3 参数传递详解
4. 高级特性
4.1 返回栈管理
4.2 深层链接(Deep Link)
4.3 底部导航栏集成
5. 实际项目应用
5.1 单 Activity + 多 Fragment 架构
5.2 多模块化项目
5.3 动态导航
6. 最佳实践与注意事项
7. 常见问题与解决方案
7.1 Fragment 重复创建
7.2 参数丢失
7.3 返回栈异常
8. 总结
上一篇:
Android Jetpack 组件库 ->Jetpack Navigation (上)_android jetpack navigation-CSDN博客
1. Navigation 简介与背景
1.1 为什么需要 Navigation?
传统导航的问题:
// 传统方式 - 手动管理 Fragmentclass MainActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)// 手动添加 FragmentsupportFragmentManager.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 的优势
// Navigation 方式 - 声明式导航// 在导航图中定义<actionandroid: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:添加依赖
// build.gradle (app)dependencies {def nav_version = "2.7.5"// Navigation Fragmentimplementation "androidx.navigation:navigation-fragment-ktx:$nav_version"implementation "androidx.navigation:navigation-ui-ktx:$nav_version"// Navigation TestingandroidTestImplementation "androidx.navigation:navigation-testing:$nav_version"}// build.gradle (project)plugins {id "androidx.navigation.safeargs.kotlin" version "2.7.5"}
步骤2:创建导航图
<!-- 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"><!-- 首页 --><fragmentandroid:id="@+id/homeFragment"android:name="com.example.app.HomeFragment"android:label="首页"><actionandroid:id="@+id/action_home_to_detail"app:destination="@id/detailFragment"app:enterAnim="@anim/slide_in_right"app:exitAnim="@anim/slide_out_left" /><actionandroid:id="@+id/action_home_to_profile"app:destination="@id/profileFragment" /></fragment><!-- 详情页 --><fragmentandroid:id="@+id/detailFragment"android:name="com.example.app.DetailFragment"android:label="详情"><argumentandroid:name="itemId"app:argType="string"app:nullable="false"app:defaultValue="0" /><argumentandroid:name="itemName"app:argType="string"app:nullable="true" /><argumentandroid:name="itemPrice"app:argType="float"app:nullable="true" /></fragment><!-- 个人资料页 --><fragmentandroid:id="@+id/profileFragment"android:name="com.example.app.ProfileFragment"android:label="个人资料" /></navigation>
步骤3:设置 NavHost
<!-- 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.FragmentContainerViewandroid: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:
// 在 Fragment 中class HomeFragment : Fragment() {private lateinit var navController: NavControlleroverride fun onViewCreated(view: View, savedInstanceState: Bundle?) {super.onViewCreated(view, savedInstanceState)// 方法1:通过 findNavController()navController = findNavController()// 方法2:通过 NavHostFragmentnavController = NavHostFragment.findNavController(this)// 方法3:通过 ActivitynavController = (activity as MainActivity).findNavController(R.id.nav_host_fragment)}}
基本导航:
// 简单导航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" />
接收参数:
class DetailFragment : Fragment() {override fun onViewCreated(view: View, savedInstanceState: Bundle?) {super.onViewCreated(view, savedInstanceState)// 方法1:通过 argumentsval itemId = arguments?.getString("itemId")val itemName = arguments?.getString("itemName")// 方法2:使用 Safe Args(推荐)val args = DetailFragmentArgs.fromBundle(arguments!!)val itemId = args.itemIdval itemName = args.itemNameval itemPrice = args.itemPrice// 使用参数updateUI(itemId, itemName, itemPrice)}private fun updateUI(itemId: String, itemName: String?, itemPrice: Float?) {// 更新界面}}
4. 高级特性
4.1 返回栈管理
// 基本返回navController.popBackStack()// 返回到指定目标navController.popBackStack(R.id.homeFragment, false)// 清除返回栈并导航navController.navigate(R.id.detailFragment) {popUpTo(R.id.homeFragment) { inclusive = true }}// 设置返回栈行为<actionandroid:id="@+id/action_home_to_detail"app:destination="@id/detailFragment"app:popUpTo="@id/homeFragment"app:popUpToInclusive="false" />
4.2 深层链接(Deep Link)
应用内深层链接:
<fragmentandroid:id="@+id/detailFragment"android:name="com.example.app.DetailFragment"><deepLinkandroid:id="@+id/deepLink"app:uri="myapp://detail/{itemId}" /><argumentandroid:name="itemId"app:argType="string" /></fragment>
Web 深层链接:
<fragmentandroid:id="@+id/detailFragment"android:name="com.example.app.DetailFragment"><deepLinkapp:uri="https://myapp.com/detail/{itemId}" /><argumentandroid:name="itemId"app:argType="string" /></fragment>
处理深层链接:
// 在 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 底部导航栏集成
创建底部导航菜单:
<!-- res/menu/bottom_nav_menu.xml --><menu xmlns:android="http://schemas.android.com/apk/res/android"><itemandroid:id="@+id/homeFragment"android:icon="@drawable/ic_home"android:title="首页" /><itemandroid:id="@+id/categoryFragment"android:icon="@drawable/ic_category"android:title="分类" /><itemandroid:id="@+id/profileFragment"android:icon="@drawable/ic_profile"android:title="我的" /></menu>
集成底部导航:
<!-- 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.FragmentContainerViewandroid: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.BottomNavigationViewandroid: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 中设置:
// 监听导航变化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> 标签引入子导航图,便于团队协作和模块解耦。
<!-- 主导航图 --><navigation ...><include app:graph="@navigation/feature_a_nav" /><include app:graph="@navigation/feature_b_nav" /></navigation>
5.3 动态导航
- 可以根据业务逻辑动态决定导航目标,例如登录后跳转到不同页面。
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 使用。
- 合理设计导航图,充分利用深层链接、动画、返回栈等高级特性,打造高质量的用户体验。