前言在 ArkUI 中Stack 布局是实现层叠效果的核心容器。它允许子组件在 Z 轴方向叠加配合alignContent属性可以精确控制每个子元素的位置。“海风日记“中大量使用了 Stack 布局中间的浮动按钮叠加在 Tabs 上方、闪屏页的 Canvas 波浪叠加在渐变背景上、日记详情页的 Bottom Sheet 遮罩层、图片查看器的全屏覆盖等。本文将从这些实战案例出发深入讲解 Stack 布局的完整用法。一、Stack 组件概述1.1 Stack 的基本概念Stack是 ArkUI 中的层叠布局容器子组件在 Z 轴方向依次叠加。后声明的子组件位于上层先声明的位于下层。Stack({ alignContent: Alignment.Center }) { // 下层渐变背景 Column() .width(100%).height(100%) .linearGradient({...}) // 上层品牌内容 Column({ space: 0 }) { Text(海风日记).fontSize(42) Text(让每一天随海风飘散).fontSize(16) } }1.2 Stack 与 Column/Row 的对比对比维度StackColumnRow排列方向Z 轴层叠Y 轴垂直X 轴水平子组件位置通过 alignContent 控制自动排列自动排列适用场景层叠、覆盖、浮层列表、表单行内元素Z 轴层级支持不支持不支持1.3 Stack 的构造函数Stack(value?: StackOptions)StackOptions包含以下参数参数类型说明默认值alignContentAlignment所有子组件默认对齐方式Alignment.Center二、Alignment 对齐方式2.1 Alignment 枚举值Alignment提供了 9 种对齐方式enum Alignment { TopStart, // 左上 Top, // 上中 TopEnd, // 右上 Start, // 左中 Center, // 居中默认 End, // 右中 BottomStart, // 左下 Bottom, // 下中 BottomEnd // 右下 }2.2 海风日记中的对齐方式场景对齐方式说明闪屏页整体布局Alignment.Bottom底部对齐按钮在底部浮动按钮Alignment.Bottom底部对齐按钮在 Tab 栏上方日记详情页Alignment.TopStart左上对齐导航在顶部Bottom Sheet 遮罩默认居中全屏遮罩覆盖2.3 子组件单独对齐每个子组件可以通过alignSelf单独设置对齐方式覆盖 Stack 的alignContentStack({ alignContent: Alignment.Center }) { Text(居中文本) .alignSelf(Alignment.TopStart) // 此子组件单独左上对齐 .position({ x: 10, y: 10 }) // 进一步偏移 }三、浮动按钮的 Stack 实现3.1 完整源码“海风日记“中间的浮动按钮通过 Stack 叠加在 Tabs 上方Stack({ alignContent: Alignment.Bottom }) { // ── 下层Tabs 导航 ─────────────────────── Tabs({ barPosition: BarPosition.End, index: this.currentIndex }) { TabContent() { HomeTab() } .tabBar(this.tabBuilder(0, 首页, $r(sys.symbol.house))) TabContent() { CalendarTab() } .tabBar(this.tabBuilder(1, 日历, $r(sys.symbol.calendar))) TabContent() { Column().width(100%).height(100%).backgroundColor(COLOR_BG_PAGE) } .tabBar(this.centerTabBar()) TabContent() { ReviewTab() } .tabBar(this.tabBuilder(3, 回顾, $r(sys.symbol.clock))) TabContent() { MineTab() } .tabBar(this.tabBuilder(4, 我的, $r(sys.symbol.person))) } .width(100%).height(100%) // ── 上层中间浮动按钮 ───────────────── Button() { SymbolGlyph($r(sys.symbol.plus)) .fontSize(26).fontColor([#FFFFFF]) } .width(56).height(56).borderRadius(28) .backgroundColor(#F5A623) .shadow({ radius: 16, color: rgba(245,166,35,0.40), offsetX: 0, offsetY: 4 }) .margin({ bottom: TAB_BAR_HEIGHT / 2 - 28 }) .onClick(() { router.pushUrl({ url: pages/diary/WriteDiaryPage }) }) } .width(100%).height(100%)3.2 定位原理浮动按钮的定位通过margin实现而非positionmargin({ bottom: TAB_BAR_HEIGHT / 2 - 28 }) 83 / 2 - 28 13.5这种定位方式利用了 Stack 的Alignment.Bottom对齐按钮底部距离 Stack 底部 13.5vp而 Tab 栏高度为 83vp所以按钮的垂直中心正好与 Tab 栏的中心线对齐。3.3 margin vs position 的对比定位方式优点缺点推荐场景margin不影响布局流定位精度有限微调位置position精确定位脱离布局流需要精确坐标offset保留原始位置视觉偏移动画效果3.4 浮动按钮与 Tab 切换的交互// 点击浮动按钮 .onClick(() { router.pushUrl({ url: pages/diary/WriteDiaryPage }) }) // 点击 Tab 栏中间位 .onChange((index: number) { if (index 2) { router.pushUrl({ url: pages/diary/WriteDiaryPage }) } else { this.currentIndex index } })两种触发方式指向同一个行为确保无论用户点击按钮还是 Tab 栏都能正确跳转。四、遮罩层的 Stack 实现4.1 Bottom Sheet 遮罩“海风日记“的日记详情页使用 Stack 实现 Bottom Sheet 遮罩Builder actionSheetOverlay() { Stack() { // ── 遮罩层 ────────────────────────────── Column() .width(100%).height(100%) .backgroundColor(rgba(0,0,0,0.45)) // 半透明黑色遮罩 .onClick(() { this.showActionSheet false }) // 点击遮罩关闭 // ── Bottom Sheet ────────────────────────── Column({ space: 8 }) { // 操作列表 Column() { this.actionItem(✏️, #FFF3E0, 编辑日记, false, ...) Divider().color(#F5F5F5).margin({ left: 54 }) this.actionItem(, #E8F4FF, 分享日记, false, ...) Divider().color(#F5F5F5).margin({ left: 54 }) this.actionItem(, #F0F0F5, 设为私密, false, ...) Divider().color(#F5F5F5).margin({ left: 54 }) this.actionItem(, #F5FFF0, 复制文本, false, ...) this.actionItem(️, #FFF0F0, 删除日记, true, ...) } .backgroundColor(#FFFFFF).borderRadius(14) // 取消按钮 Text(取消) .width(100%).fontSize(17).fontColor(COLOR_PRIMARY) .fontWeight(FontWeight.Bold) .textAlign(TextAlign.Center) .padding(16) .backgroundColor(#FFFFFF).borderRadius(14) .onClick(() { this.showActionSheet false }) } .width(100%) .padding({ left: 12, right: 12, bottom: 34 }) } .width(100%).height(100%) }4.2 遮罩的交互逻辑遮罩层的交互逻辑包含三个要点点击遮罩关闭遮罩层的onClick关闭 Bottom Sheet操作项点击执行对应操作后关闭 Bottom Sheet取消按钮专门提供关闭按钮// 三种关闭方式 // 1. 点击遮罩 Column() .onClick(() { this.showActionSheet false }) // 2. 操作项 this.actionItem(✏️, #FFF3E0, 编辑日记, false, () { this.showActionSheet false router.pushUrl({ url: pages/diary/EditDiaryPage }) }) // 3. 取消按钮 Text(取消) .onClick(() { this.showActionSheet false })4.3 遮罩透明度设计遮罩的透明度直接影响用户体验透明度值效果适用场景0.30较浅可以看到底层内容轻提示0.45适中推荐操作菜单0.60较深强调上层内容重要确认0.80几乎看不到底层全屏模态五、闪屏页的 Stack 多层叠加5.1 闪屏页的完整布局“海风日记“的闪屏页使用了三层 Stack 叠加Stack (最外层) ├── 第一层全屏渐变背景 │ Column().linearGradient(...) │ ├── 第二层Canvas 波浪动画 │ Canvas(this.ctx).drawWaves() │ ├── 第三层品牌内容区 │ Column { │ Text(海风日记) │ Text(让每一天随海风飘散) │ Text() │ } │ └── 第四层底部登录按钮 Column { Button(登录海风日记) Text(直接体验 →) }5.2 闪屏页源码Stack({ alignContent: Alignment.Bottom }) { // ── 背景渐变 ───────────────────────────── Column() .width(100%).height(100%) .linearGradient({ angle: 160, colors: [ [#FFE4B0, 0.0], [#FFD080, 0.35], [#F5A623, 0.70], [#C8800A, 1.0], ] }) // ── 波浪 Canvas ────────────────────────── Canvas(this.ctx) .width(100%).height(300) .onReady(() { this.drawWaves() }) // ── 品牌内容区 ─────────────────────────── Column({ space: 0 }) { Text(海风日记).fontSize(42).fontColor(#FFFFFF) Text(让每一天随海风飘散).fontSize(16) Text().fontSize(32) } .layoutWeight(1) .justifyContent(FlexAlign.Center) // ── 底部登录按钮区 ─────────────────────── Column({ space: 16 }) { Button(登录海风日记) .width(85%).height(52) .fontColor(#C8800A).backgroundColor(#FFFFFF) .borderRadius(26) .onClick(() { router.replaceUrl({ url: pages/login/LoginPhonePage }) }) Text(直接体验 →) .onClick(() { router.replaceUrl({ url: pages/Index }) }) } .padding({ bottom: 20 }) } .width(100%).height(100%)5.3 多层 Stack 的 Z 轴顺序在 Stack 中Z 轴顺序由声明顺序决定Z 轴方向从下到上 ↑ | 第四层底部按钮最后声明最上层 | 第三层品牌内容 | 第二层Canvas 波浪 | 第一层渐变背景最先声明最下层 六、WaveHeaderBand 的 Stack 实现6.1 组件布局WaveHeaderBand组件也使用了 Stack 多层叠加Stack({ alignContent: Alignment.Bottom }) { // ── 第一层海天渐变背景 ──────────────── Column() { Text(this.tagline) Text(this.greeting) this.content() } .linearGradient({...}) // ── 第二层地平线光晕 ────────────────── Rect() .width(100%).height(2) .linearGradient({ colors: [ [rgba(245,166,35,0), 0], [rgba(245,166,35,0.35), 0.25], [rgba(255,200,80,0.55), 0.5], [rgba(245,166,35,0.35), 0.75], [rgba(245,166,35,0), 1.0] ] }) .margin({ bottom: 26 }) // ── 第三层波浪底边 ──────────────────── Shape() { Path() .commands(M 0 12 C 65 2 130 22 195 12 C 260 2 325 22 390 12 L 390 24 L 0 24 Z) .fill(COLOR_BG_PAGE) } .width(100%).height(24) }6.2 三层结构的视觉层次第三层顶部波浪底边过渡到页面背景 第二层中间地平线光晕模拟日出光线 第一层底部海天渐变背景品牌色展示6.3 地平线光晕的视觉效果地平线光晕通过渐变的 Rect 实现模拟日出时的光线效果Rect() .width(100%) .height(2) // 仅 2vp 高极细的线条 .linearGradient({ angle: 90, colors: [ [rgba(245,166,35,0), 0], // 左端透明 [rgba(245,166,35,0.35), 0.25], // 渐显 [rgba(255,200,80,0.55), 0.5], // 中央最亮 [rgba(245,166,35,0.35), 0.75], // 渐隐 [rgba(245,166,35,0), 1.0] // 右端透明 ] })七、Stack 的进阶用法7.1 position 属性Stack 子组件可以通过position属性实现绝对定位Stack() { // 背景 Column().width(100%).height(100%).backgroundColor(#FFF8F0) // 右上角跳过按钮 Button(跳过) .position({ top: 52, right: 20 }) // 绝对定位 .onClick(() { router.replaceUrl({ url: pages/Index }) }) // 居中内容 Column({ space: 12 }) { Text(欢迎使用海风日记).fontSize(24) Text(让每一天随海风飘散).fontSize(15) } .alignSelf(Alignment.Center) }7.2 zIndex 控制层级使用zIndex可以显式控制子组件的层级Stack() { Column() .zIndex(1) // 层级 1 .backgroundColor(#FFE4B0) Column() .zIndex(3) // 层级 3在最上层 .backgroundColor(#F5A623) Column() .zIndex(2) // 层级 2 .backgroundColor(#C8800A) }7.3 clip 裁剪clip属性控制 Stack 是否裁剪超出边界的子组件Stack() .clip(true) // 裁剪超出部分 // 或 .clip(false) // 不裁剪允许内容溢出7.4 Stack 与 Animation 配合Stack 常与动画配合实现过渡效果Stack() { // 背景内容 Column() { ... } // 动画浮层 Column() { ... } .opacity(this.showOverlay ? 1 : 0) .animation({ duration: 300 }) }八、常见问题与最佳实践8.1 子组件不显示问题Stack 中的子组件没有显示出来。原因子组件没有设置width和height或者尺寸为 0。解决方案确保每个子组件都有明确的尺寸Stack() { Column() .width(100%).height(100%) // 明确尺寸 .backgroundColor(#FFE4B0) }8.2 层叠顺序错误问题期望的浮层被其他组件遮挡。原因声明顺序错误后声明的在上层。解决方案调整声明顺序将浮层放在最后声明Stack() { // 正确的顺序底层内容先声明 Tabs(...) { ... } // 浮层后声明在上层 Button() { ... } }8.3 position 定位超出边界问题使用position定位的元素超出 Stack 边界。解决方案使用clip(false)允许内容溢出或调整 positioning 值Stack() .clip(false) // 允许内容溢出 // 或 .height(100%) // 确保有足够的空间总结本文通过“海风日记“的实战案例深入讲解了 Stack 布局的完整用法Stack 基础Z 轴层叠容器的基本概念和与 Column/Row 的对比Alignment 对齐9 种对齐方式及子组件单独对齐浮动按钮通过 Stack 底部对齐 margin 偏移实现中间按钮悬浮遮罩层Bottom Sheet 的半透明遮罩实现闪屏页多层叠加渐变背景、Canvas 波浪、品牌内容、底部按钮的四层结构WaveHeaderBand渐变背景、光晕、波浪的三层设计进阶用法position、zIndex、clip、动画配合下一篇文章将深入讲解模块配置与路由注册包括 module.json5 和 main_pages.json 的完整配置敬请期待。如果这篇文章对你有帮助欢迎点赞、收藏⭐、关注你的支持是我持续创作的动力相关资源Stack 容器组件文档Alignment 对齐文档position 属性文档海风日记项目源码[HarmonyOS 开发者官网](https://atomgit.com/openharmony/docs开源鸿蒙跨平台社区ArkUI 布局概述ArkUI 动画概述