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 使用。
  • 合理设计导航图,充分利用深层链接、动画、返回栈等高级特性,打造高质量的用户体验。
相关推荐
阿巴斯甜1 天前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker1 天前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq95271 天前
Andorid Google 登录接入文档
android
黄林晴1 天前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab2 天前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿2 天前
Android MediaPlayer 笔记
android
Jony_2 天前
Android 启动优化方案
android
阿巴斯甜2 天前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android
张小潇2 天前
AOSP15 Input专题InputReader源码分析
android
_小马快跑_2 天前
Kotlin | 协程调度器选择:何时用CoroutineScope配置,何时用launch指定?
android