Android Jetpack 组件库 ->Jetpack Navigation (下)

目录

[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博客

传统导航的问题:

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()

    }

}

问题:

  • 代码重复且容易出错
  • 难以管理返回栈
  • 参数传递不安全
  • 难以处理深层链接
  • 测试困难
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 (导航目标)

详细说明:

  1. Navigation Graph(导航图)
  • 定义应用的所有导航路径
  • 可视化编辑界面
  • XML 格式存储
  1. NavController(导航控制器)
  • 管理导航操作
  • 处理返回栈
  • 执行导航动作
  1. NavHost(导航容器)
  • 显示当前目标
  • FragmentContainerView 实现
  • 管理 Fragment 生命周期
  1. 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. 最佳实践与注意事项

  1. 优先使用 Safe Args
  • 避免 Bundle 传参出错,提升类型安全。
  1. 合理设计导航图结构
  • 避免过深嵌套,保持导航图清晰。
  1. 处理返回栈
  • 善用 popUpTo 和 inclusive,避免页面堆积。
  1. 深层链接支持
  • 方便外部唤起和 App 内跳转。
  1. 动画与转场
  • 提升用户体验,使用自定义动画资源。
  1. 测试导航逻辑
  • 使用 Navigation Testing 库进行单元测试和 UI 测试。

7. 常见问题与解决方案

7.1 Fragment 重复创建

  • 避免在导航时重复 navigate 到同一个目标,可以先判断当前 destination。

7.2 参数丢失

  • 使用 Safe Args,避免 Bundle 传参遗漏或类型错误。

7.3 返回栈异常

  • 检查导航图中 popUpTo 配置,确保返回栈符合预期。
  • 多 NavHost 时要分别管理各自的 NavController,避免混淆。

8. 总结

Jetpack Navigation 组件极大简化了 Android 应用的导航开发,提升了代码的可维护性和安全性。

  • 推荐在新项目中优先采用 Navigation 组件,配合单 Activity 架构和 Safe Args 使用。
  • 合理设计导航图,充分利用深层链接、动画、返回栈等高级特性,打造高质量的用户体验。
相关推荐
用户2070386194919 分钟前
StateFlow与SharedFlow如何取舍?
android
QmDeve21 分钟前
原生Android Java调用系统指纹识别方法
android
淹没22 分钟前
🚀 告别复杂的HTTP模拟!HttpHook让Dart应用测试变得超简单
android·flutter·dart
HX4361 小时前
MP - List (not just list)
android·ios·全栈
安卓开发者2 小时前
Android WorkManager 详解:高效管理后台任务
android
henysugar4 小时前
便捷删除Android开发中XML中重复字符串资源的一个办法
android·xml
aqi004 小时前
FFmpeg开发笔记(七十八)采用Kotlin+Compose的NextPlayer播放器
android·ffmpeg·音视频·直播·流媒体
你过来啊你5 小时前
Android开发中技术选型的落地方案
android·技术选型