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

目录

1. Navigation 简介与背景

1.1 为什么需要 Navigation?

1.2 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 返回栈异常

7.4 多 NavHost 场景

8. 总结


上一篇:

Android Jetpack 组件库 ->Jetpack Navigation (上)_android jetpack navigation-CSDN博客

1. Navigation 简介与背景

1.1 为什么需要 Navigation?

传统导航的问题:

 

// 传统方式 - 手动管理 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 的优势

// 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:添加依赖

// 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:创建导航图

<!-- 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

<!-- 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:

// 在 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)

    }

}

基本导航:

// 简单导航

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:通过 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 返回栈管理

// 基本返回

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)

应用内深层链接:

<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 深层链接:

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

处理深层链接:

// 在 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">

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

集成底部导航:

<!-- 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 中设置:

        // 监听导航变化

        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. 最佳实践与注意事项

  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 配置,确保返回栈符合预期。

7.4 多 NavHost 场景

  • 多 NavHost 时要分别管理各自的 NavController,避免混淆。

8. 总结

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

  • 推荐在新项目中优先采用 Navigation 组件,配合单 Activity 架构和 Safe Args 使用。
  • 合理设计导航图,充分利用深层链接、动画、返回栈等高级特性,打造高质量的用户体验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值