一、前言:应用架构的第一性原理
做鸿蒙应用开发,最容易被忽视但也最重要的事情是:先把应用外壳(AppShell)搭对。
很多开发者上来就写业务页面,结果发现:
标题栏风格不统一,有的手写、有的用系统
页面返回逻辑混乱,Router 和 Navigation 混用
Tab 切换和页面跳转的边界模糊
底部导航栏和内容滚动相互影响
本文将以ArkUILab项目的三个核心实验为线索,从零讲解如何构建一个接近官方体验的鸿蒙应用外壳。我们将覆盖三个关键能力:
Navigation AppShell(E001):页面层级、动态模糊标题栏、生命周期
Floating TabBar(E003):悬浮底栏、MiniBar、Tab 内独立导航栈
Page Transition(E002):页面转场体验、共享元素的探索与局限
二、Navigation 应用外壳
2.1 Navigation 在 HarmonyOS 设计体系中的位置
在深入代码之前,先理解 Navigation 在鸿蒙 UI Design Kit 中的定位非常重要。它不是一个"能跳转页面的组件",而是一个把页面层级、标题栏、内容区和系统化外壳绑在一起的结构入口。
在 ArkUI 的能力体系中,Navigation 的合理边界是:
Navigation owns: 页面层级 / 标题栏 / 内容区 / 页面级命令 / 返回模型 does NOT own: 工作区树 / Tab 语义 / 编辑器块 / 对象操作 / 数据模型
官方设计体系中的能力分工:
| 能力 | 职责 | 与 Navigation 的关系 |
|---|---|---|
| Navigation | 页面层级与返回路径 | 上游结构,定义"当前在哪" |
| Sidebar | 主导航树和长期结构 | 内容区的承载者之一 |
| Tabs | 同层内容切换 | 应依附在 Navigation 确定的层级内 |
| ActionBar | 命令集合 | 不同于页面级菜单,是操作集 |
| Search | 全局/局部搜索 | 可放入标题栏扩展区,但能力独立 |
2.2 为什么用 HdsNavigation 而不是普通 Navigation?
普通Navigation组件是 ArkUI 的基础导航容器,但HdsNavigation(来自@kit.UIDesignKit)提供了官方级的额外能力:
内置动态模糊标题栏
标题栏与滚动内容绑定
标题栏菜单样式
沉浸式渐变模糊
系统级返回按钮样式
// 错误写法 ❌ Navigation(this.pathStack) { // ... } // 正确写法 ✅ HdsNavigation(this.pathStack) { // ... }HDS Kit 位于 DevEco SDK 的default/hms下,通过@kit.UIDesignKit导入,不需要在entry/oh-package.json5中追加 ohpm 依赖。
2.3 最小可用 Navigation AppShell
一个完整的 Navigation AppShell 结构:
Index → NavigationAppShell → HdsNavigation(pathStack) → Home Scroll(homeScroller) → 固定高度 Header (136vp) → 可滚动内容 → navDestination(name) → HdsNavDestination → Detail Scroll(detailScroller) → titleBar (动态模糊) → bindToScrollable(detailScroller)
核心原则:
Shell 拥有 NavigationStack:
NavPathStack定义在 Shell 组件中Page 拥有自己的 Scroll:Root 和 Detail 各自独立的
ScrollerTitleBar 由 HDS 拥有:不手写标题栏和返回按钮
返回由 HDS/Navigation 拥有:不自己处理返回逻辑
内容只负责提供可滚动、可采样的页面材质
2.4 动态模糊标题栏
这是官方设置、文件、图库、备忘录等应用的标志性体验——标题栏不是固定的实色工具条,而是随内容滚动从透明渐变到模糊:
HdsNavigation(this.pathStack) { // 内容 } .titleMode(HdsNavigationTitleMode.MINI) .titleBar({ style: { scrollEffectOpts: { scrollEffectType: ScrollEffectType.IMMERSIVE_GRADIENT_BLUR, originalStyle: { // 初始态:透明背景 backgroundColor: '#00FFFFFF', contentStyle: { menuStyle: { backgroundColor: '#FFFFFFCC', textColor: '#182431', iconColor: '#182431' } } }, scrollEffectStyle: { // 滚动后:半透明白底 + 模糊 backgroundColor: '#CCFFFFFF', blurRadius: 18,