Android Navigation Fragment 导航实战

获取 NavController:

kotlin 复制代码
// 方式1:在 Activity 中获取
class MainActivity : AppCompatActivity() {
    private lateinit var navController: NavController
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
        // 从 NavHostFragment 获取 NavController
        val navHostFragment = supportFragmentManager
            .findFragmentById(R.id.navHostFragment) as NavHostFragment
        navController = navHostFragment.navController
    }
}

// 方式2:使用扩展函数
class MyFragment : Fragment() {
    private val navController by lazy {
        findNavController()
    }
}

// 方式3:使用 View 扩展函数
class MyFragment : Fragment() {
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        
        val navController = view.findNavController()
    }
}

8.3.2 基本导航操作

导航到不同的 Fragment:

kotlin 复制代码
class HomeFragment : Fragment() {
    private val navController by lazy { findNavController() }
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        // 导航到用户详情页
        btnUserDetail.setOnClickListener {
            navController.navigate(R.id.action_homeFragment_to_userDetailFragment)
        }
        // 导航到设置页
        btnSettings.setOnClickListener {
            navController.navigate(R.id.action_homeFragment_to_settingsFragment)
        }
    }
}

8.3.3 数据传递

方式1:使用 Bundle 传递参数:

kotlin 复制代码
// 发送数据
class HomeFragment : Fragment() {
    private val navController by lazy { findNavController() }
    fun navigateToUserDetail(userId: String, userName: String) {
        val bundle = bundleOf(
            "userId" to userId,
            "userName" to userName
        )
        navController.navigate(
            R.id.action_homeFragment_to_userDetailFragment,
            bundle
        )
    }
}

// 接收数据
class UserDetailFragment : Fragment() {
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        // 从 arguments 获取数据
        val userId = arguments?.getString("userId")
        val userName = arguments?.getString("userName")
        
        updateUI(userId, userName)
    }
}

方式2:使用 Safe Args(推荐):

gradle 复制代码
// build.gradle (app\工程) 
plugins {
    id 'com.android.application'
    id 'org.jetbrains.kotlin.android'
    id 'androidx.navigation.safeargs.kotlin' // 添加 Safe Args 插件 版本使用2.8.0
}
xml 复制代码
<!-- 在导航图中定义参数 -->
<fragment
    android:id="@+id/userDetailFragment"
    android:name="com.example.app.ui.user.UserDetailFragment"
    android:label="User Detail">
    <argument
        android:name="userId"
        app:argType="string" />
    <argument
        android:name="userName"
        app:argType="string"
        app:nullable="true" />
</fragment>
kotlin 复制代码
// 发送数据(使用 Safe Args)
class HomeFragment : Fragment() {
    private val navController by lazy { findNavController() }
    fun navigateToUserDetail(userId: String, userName: String) {
        val direction = HomeFragmentDirections
            .actionHomeFragmentToUserDetailFragment(userId, userName)
        navController.navigate(direction)
    }
}

// 接收数据(使用 Safe Args)
class UserDetailFragment : Fragment() {
    private val args: UserDetailFragmentArgs by navArgs()
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        
        val userId = args.userId
        val userName = args.userName
        
        updateUI(userId, userName)
    }
}

8.3.4 返回堆栈管理

popUpTo 和 popUpToInclusive:

kotlin 复制代码
class HomeFragment : Fragment() {
    private val navController by lazy { findNavController() }
    fun navigateToProfileAndClearBackstack() {
        // 导航到个人中心,并清空返回堆栈到首页
        navController.navigate(
            R.id.action_homeFragment_to_profileFragment
        )
    }
    
    fun navigateToLoginAndClearBackstack() {
        // 导航到登录页,并清空整个返回堆栈
        navController.navigate(
            R.id.action_homeFragment_to_loginFragment
        )
    }
}

XML 配置返回堆栈管理:

xml 复制代码
<action
    android:id="@+id/action_home_to_profile"
    app:destination="@id/profileFragment"
    app:popUpTo="@id/homeFragment"
    app:popUpToInclusive="false" />

<action
    android:id="@+id/action_home_to_login"
    app:destination="@id/loginFragment"
    app:popUpTo="@id/homeFragment"
    app:popUpToInclusive="true" />

8.4 高级导航功能

8.4.1 深层链接(Deep Link)

深层链接的概念 :

深层链接允许用户直接导航到应用中的特定目的地,无需经过首页。
配置深层链接:

xml 复制代码
<fragment
    android:id="@+id/userDetailFragment"
    android:name="com.example.app.ui.user.UserDetailFragment"
    android:label="User Detail">
    <!-- 深层链接1:app://user/{userId} -->
    <deepLink
        app:uri="app://user/{userId}"
        app:action="android.intent.action.VIEW"
        app:mimeType="text/html" />
    
    <!-- 深层链接2:https://www.example.com/user/{userId} -->
    <deepLink
        app:uri="https://www.example.com/user/{userId}"
        app:action="android.intent.action.VIEW"
        app:mimeType="text/html" />
    <argument
        android:name="userId"
        app:argType="string" />
</fragment>

在 AndroidManifest.xml 中声明深层链接:

xml 复制代码
<activity
    android:name=".MainActivity"
    android:exported="true">
    
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        
        <!-- 深层链接1 -->
        <data
            android:scheme="app"
            android:host="user" />
        
        <!-- 深层链接2 -->
        <data
            android:scheme="https"
            android:host="www.example.com"
            android:pathPrefix="/user" />
    </intent-filter>
    
    <!-- 导航图 -->
    <nav-graph android:value="@navigation/nav_graph" />
</activity>

处理深层链接:

kotlin 复制代码
class MainActivity : AppCompatActivity() {
    
    private lateinit var navController: NavController
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
        val navHostFragment = supportFragmentManager
            .findFragmentById(R.id.navHostFragment) as NavHostFragment
        navController = navHostFragment.navController
        
        // 处理深层链接
        navController.handleDeepLink(intent)
    }
    
    override fun onNewIntent(intent: Intent) {
        super.onNewIntent(intent)
        
        // 处理新的深层链接
        navController.handleDeepLink(intent)
    }
}

8.4.2 嵌套导航图

嵌套导航图的概念 :

嵌套导航图允许将相关的目的地组织在一起,形成子导航图。

创建嵌套导航图:

xml 复制代码
<!-- navigation/nested_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/nested_graph"
    app:startDestination="@id/step1Fragment">

    <!-- 步骤1 -->
    <fragment
        android:id="@+id/step1Fragment"
        android:name="com.example.app.ui.onboarding.Step1Fragment"
        android:label="Step 1">
        
        <action
            android:id="@+id/action_step1_to_step2"
            app:destination="@id/step2Fragment" />
    </fragment>

    <!-- 步骤2 -->
    <fragment
        android:id="@+id/step2Fragment"
        android:name="com.example.app.ui.onboarding.Step2Fragment"
        android:label="Step 2">
        
        <action
            android:id="@+id/action_step2_to_step3"
            app:destination="@id/step3Fragment" />
    </fragment>

    <!-- 步骤3 -->
    <fragment
        android:id="@+id/step3Fragment"
        android:name="com.example.app.ui.onboarding.Step3Fragment"
        android:label="Step 3">
        
        <action
            android:id="@+id/action_step3_to_complete"
            app:destination="@id/homeFragment"
            app:popUpTo="@id/nested_graph"
            app:popUpToInclusive="true" />
    </fragment>

</navigation>
xml 复制代码
<!-- 在主导航图中引用嵌套导航图 -->
<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/onboardingFragment">

    <!-- 嵌套导航图 -->
    <include app:graph="@navigation/nested_graph" />

    <!-- 首页 -->
    <fragment
        android:id="@+id/homeFragment"
        android:name="com.example.app.ui.home.HomeFragment"
        android:label="Home" />

</navigation>

8.4.3 转场动画

定义转场动画:

xml 复制代码
<!-- res/anim/slide_in_right.xml -->
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate
        android:duration="300"
        android:fromXDelta="100%"
        android:toXDelta="0%" />
</set>

<!-- res/anim/slide_out_left.xml -->
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate
        android:duration="300"
        android:fromXDelta="0%"
        android:toXDelta="-100%" />
</set>

<!-- res/anim/slide_in_left.xml -->
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate
        android:duration="300"
        android:fromXDelta="-100%"
        android:toXDelta="0%" />
</set>

<!-- res/anim/slide_out_right.xml -->
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate
        android:duration="300"
        android:fromXDelta="0%"
        android:toXDelta="100%" />
</set>

在导航图中应用转场动画:

xml 复制代码
<action
    android:id="@+id/action_home_to_detail"
    app:destination="@id/detailFragment"
    app:enterAnim="@anim/slide_in_right"
    app:exitAnim="@anim/slide_out_left"
    app:popEnterAnim="@anim/slide_in_left"
    app:popExitAnim="@anim/slide_out_right" />

核心要点回顾

  1. 导航基础

    • 传统导航方式的痛点
    • Navigation 组件的优势
  2. Navigation Graph 设计

    • 导航图的基本结构
    • 目的地类型
    • 动作配置
  3. Fragment 导航实战

    • NavController 的使用
    • 基本导航操作
    • 数据传递(Bundle 和 Safe Args)
    • 返回堆栈管理
  4. 高级导航功能

    • 深层链接
    • 嵌套导航图
    • 转场动画

5使用

并且包含:

  • 页面跳转(navigate)
  • 参数传递(Safe Args)或者Bundle
  • 返回上一页(navigateUp)
  • 从详情页跳到设置页并清理部分返回栈(popUpTo)

1) 添加依赖与插件

gradle 复制代码
// app/build.gradle //
plugins {
    id 'com.android.application'
    id 'org.jetbrains.kotlin.android'
    id 'androidx.navigation.safeargs.kotlin' 
}

dependencies {
    implementation "androidx.navigation:navigation-fragment-ktx:2.8.5"
    implementation "androidx.navigation:navigation-ui-ktx:2.8.5"
}

需求背景,动手实践

我们需要构建一个电商应用的导航系统,包含:

  1. 首页、分类、购物车、我的(底部导航)
  2. 商品列表、商品详情、搜索(主导航)
  3. 登录、注册、设置(用户中心)
  4. 订单列表、订单详情(订单管理)
设计导航结构

导航架构图:

复制代码
MainActivity (底部导航)
├── HomeFragment (首页)
│   ├── ProductListFragment (商品列表)
│   └── ProductDetailFragment (商品详情)
├── CategoryFragment (分类)
├── CartFragment (购物车)
└── ProfileFragment (我的)
    ├── LoginFragment (登录)
    ├── RegisterFragment (注册)
    ├── SettingsFragment (设置)
    └── OrderListFragment (订单列表)
        └── OrderDetailFragment (订单详情)

<?xml version="1.0" encoding="utf-8"?>

复制代码
<!-- 顶部工具栏 -->
<androidx.appcompat.widget.Toolbar
    android:id="@+id/toolbar"
    android:layout_width="match_parent"
    android:layout_height="?attr/actionBarSize"
    android:background="@color/purple_500"
    android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" />

<!-- NavHostFragment 容器 -->
<androidx.fragment.app.FragmentContainerView
    android:id="@+id/nav_host_fragment"
    android:name="androidx.navigation.fragment.NavHostFragment"
    android:layout_width="match_parent"
    android:layout_height="0dp"
    android:layout_weight="1"
    app:defaultNavHost="true"
    app:navGraph="@navigation/nav_graph" />

<!-- 底部导航栏 (可选) -->
<com.google.android.material.bottomnavigation.BottomNavigationView
    android:id="@+id/bottom_nav"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:menu="@menu/bottom_nav_menu" />
相关推荐
Dovis(誓平步青云)3 小时前
家里设备越来越多,如何用一张空间地图控制灯光和温度![
android·java·前端·javascript·人工智能·电脑
晚风叙码5 小时前
MySQL 数据类型详解:从数值到字符串,一篇讲透
android·mysql·adb
传奇开心果编程5 小时前
【Compose Multiplatform 跨端开发学与练】第3课 布局与组件
android·windows·学习·ui·ios·kotlin·composer
事圆则缓7 小时前
Kotlin 入门与面试:从空安全、扩展函数到协程
安全·面试·kotlin
传奇开心果编程8 小时前
【Compose Multiplatform 跨端开发学与练】第8课 资源管理与主题
android·windows·学习·ios·kotlin·web·composer
传奇开心果编程9 小时前
【Compose Multiplatform 跨端开发学与练】第9课 测试与调试
android·学习·macos·ios·kotlin·web·composer
传奇开心果编程10 小时前
【Compose Multiplatform 跨端开发学与练】第4课 导航与路由
android·windows·学习·ui·ios·kotlin·composer
传奇开心果编程10 小时前
【Compose Multiplatform 跨端开发学与练】第6课 状态管理与架构
android·学习·ui·ios·架构·kotlin·composer
事圆则缓10 小时前
Android AOSP 定制常见概念:源码目录、系统镜像与刷机流程
android
传奇开心果编程10 小时前
【Compose Multiplatform 跨端开发学与练】第2课 Compose 基础语法
android·windows·学习·ui·ios·kotlin·composer