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" />
相关推荐
雨白1 小时前
使用 Kotlin 与 Spring Boot 从零搭建 Web 应用
spring boot·kotlin
Fate_I_C2 小时前
Adroid Data Binding数据绑定对比(findViewXX、ButterKnife)
android·kotlin·databinding
黑心的奥利奥2 小时前
WeeX跨平台框架,自定义安卓平台MarkDown文本渲染组件高度跟随内容自适应实现思路探索
android
KIHU快狐3 小时前
快狐KIHU|110寸壁挂触控一体机G+G电容屏安卓系统汽车展厅查询展示
android·python·汽车
Fate_I_C4 小时前
Android DataBinding数据绑定表达式、双向绑定
android·kotlin·databinding
csj504 小时前
安卓基础之《(29)—消息机制与异步任务》
android
张风捷特烈4 小时前
状态管理大乱斗#02 | Bloc 源码全面评析
android·前端·flutter
untE EADO5 小时前
MySQL错误-this is incompatible with sql_mode=only_full_group_by完美解决方案
android·sql·mysql
诸神黄昏EX5 小时前
Android Google EDLA
android