4、导航图(NavGraph)详解:可视化编辑与XML编辑、<navigation>标签、<fragment>标签、<action>标签、<argument>标签

导航图,说白了就是整个App页面跳转的地图。你想想看,没有地图你怎么导航?在单Activity架构里,NavGraph就是我们的导航地图。我个人习惯先画图再写代码,这样思路会清晰很多。

4.1 导航图的两种编辑方式

Android Studio提供了两种编辑NavGraph的方式:可视化编辑和XML编辑。我刚开始用的时候,总觉得可视化编辑有点鸡肋,后来发现其实各有千秋。

可视化编辑

在res/navigation目录下,双击你的nav_graph.xml文件,默认会打开Design视图。这里你可以看到所有页面的缩略图,用鼠标拖拽就能创建跳转关系。嗯,这里要注意:可视化编辑适合快速原型设计,但精细控制还得靠XML。

XML编辑

点击Design视图下方的Code标签,就能看到真正的XML源码。我个人更偏爱这种方式,因为你能精确控制每一个属性。曾经我在一个项目中,可视化编辑死活对不齐,最后发现是XML里少了个属性——从那以后我就习惯双视图对照着看了。

核心观点:可视化编辑适合快速搭建骨架,XML编辑适合精细调优。两者结合使用效率最高。

4.2 <navigation>标签——导航图的根节点

每个NavGraph文件最外层就是标签。它定义了导航图的命名空间和起始页面。

<?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"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/nav_graph"
    app:startDestination="@id/homeFragment">

    <!-- 这里放fragment、activity、action等 -->

</navigation>

这里有个关键属性:app:startDestination。它指定了App启动时第一个显示的页面。我记得有一次我把这个属性写错了,结果App一启动就崩溃,查了半天才发现是ID拼写错误。所以,务必检查startDestination的ID是否存在

4.3 <fragment>标签——定义页面节点

每个Fragment对应一个页面节点。在NavGraph里,我们用标签来声明它。

<fragment
    android:id="@+id/homeFragment"
    android:name="com.example.app.ui.home.HomeFragment"
    android:label="首页"
    tools:layout="@layout/fragment_home">

    <argument
        android:name="userId"
        app:argType="string"
        android:defaultValue="0" />

    <action
        android:id="@+id/action_home_to_detail"
        app:destination="@id/detailFragment" />

</fragment>

这里我解释几个关键属性:

  • android:id:唯一标识,用于跳转时引用。命名建议用"页面名+Fragment"的格式,比如homeFragment、detailFragment。
  • android:name:Fragment的全限定类名。注意要写完整包名路径。
  • android:label:页面标题,通常用于Toolbar显示。
  • tools:layout:预览布局,方便可视化编辑时看到页面样子。
小技巧:tools:layout属性只在编辑时生效,不会影响运行时。我习惯每个Fragment都加上这个属性,这样可视化编辑时能看到页面内容,而不是一个灰色的方块。

4.4 <action>标签——定义跳转动作

Action是导航图里的"箭头",它定义了从一个页面到另一个页面的跳转关系。每个action都包含一个目标页面的ID。

<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"
    app:popUpTo="@id/homeFragment"
    app:popUpToInclusive="true" />

Action的属性很多,我挑几个重点说:

属性 作用 我的经验
app:destination 目标页面的ID 必须存在,否则编译报错
app:enterAnim/exitAnim 进入/退出动画 建议统一管理动画资源
app:popUpTo 回退到指定页面 常用于登录后清除返回栈
app:popUpToInclusive 是否包含目标页面 设为true时,目标页面也会被弹出

我曾经在一个电商项目里,用户从首页跳到商品详情,再从详情跳到支付。支付成功后需要回到首页,并且清除中间的所有页面。这时候app:popUpTo就派上用场了——设置popUpTo="@id/homeFragment"popUpToInclusive="false",就能完美实现这个需求。

避坑指南:我曾经遇到过一个问题:action的ID命名不规范,导致多个action的ID冲突。建议统一命名格式:action_源页面_to_目标页面,比如action_home_to_detail

4.5 <argument>标签——传递参数

页面之间跳转往往需要传递数据。Argument标签就是用来定义参数的。

<argument
    android:name="productId"
    app:argType="integer"
    android:defaultValue="0" />

<argument
    android:name="productName"
    app:argType="string"
    android:defaultValue="未知商品" />

<argument
    android:name="price"
    app:argType="float"
    android:defaultValue="0.0" />

Argument支持的类型包括:

  • 基础类型:string、integer、float、boolean
  • 引用类型:reference(资源ID)
  • 自定义Parcelable类型
  • Serializable类型

在Fragment里获取参数也很简单:

// 使用Safe Args方式(推荐)
val args = DetailFragmentArgs.fromBundle(requireArguments())
val productId = args.productId
val productName = args.productName

// 或者直接使用NavArgs
val args: DetailFragmentArgs by navArgs()
提示:Safe Args是官方推荐的方式,它会自动生成Args类,编译时就能检查参数类型是否正确。我强烈建议使用这种方式,而不是手动解析Bundle。曾经我在一个项目里手动解析参数,结果类型写错了,运行时才崩溃——那感觉,你懂的。

4.6 知识体系总览

下面这张图展示了NavGraph的核心知识结构,我把它画成了SVG,方便你理解各个组件之间的关系:

NavGraph 知识体系 <navigation> 根节点 <fragment> 页面节点 <action> 跳转动作 <argument> 参数传递 android:id android:name android:label app:destination 动画属性 app:popUpTo android:name app:argType defaultValue 可视化编辑 + XML编辑 = 高效开发

4.7 实战建议

说了这么多,我总结几条实战中的建议:

  1. 先画图再编码:在纸上画出页面跳转关系,再在NavGraph里实现。我见过太多人直接上手写代码,结果跳转逻辑一团乱麻。
  2. 统一命名规范:ID、Action、Argument的命名要统一,方便团队协作。比如action统一用action_源_to_目标的格式。
  3. 善用Safe Args:参数传递一定要用Safe Args,编译时就能发现问题,比运行时崩溃强一万倍。
  4. 动画统一管理:把动画资源放在res/anim目录下,统一命名,方便复用。
  5. 定期清理无用节点:随着版本迭代,有些页面可能被废弃了。记得及时清理NavGraph里的无用节点,避免混淆。
最后说一句:NavGraph是单Activity架构的基石。你花时间把导航图设计好,后面的开发会顺畅很多。别嫌麻烦,磨刀不误砍柴工。

公众号:蓝海资料掘金营,微信deep3321