HarmonyOS应用开发实战:猫猫大作战-`Stack` 的 `alignContent` 9 位、`zIndex` 层序、状态遮罩叠层套路
前言前 8 篇我们拆的主菜单元素都在Column/Row里依次排列——标题在上、按钮在中、规则在下。但实战中有一类布局是「叠」而非「排」游戏页的棋盘 棋子 顶部 HUD三层叠在一起暂停时全屏遮罩盖在棋盘上弹窗居中浮在所有内容之上。这种「Z 轴叠层」就是Stack容器的主场。本篇以「猫猫大作战」游戏页的Stack根布局为锚点把Stack的alignContent9 位、zIndex层序、状态遮罩叠层套路讲透。读完本篇你将能独立写出三层叠游戏页、全屏暂停遮罩、居中浮弹窗三种叠层布局。提示本系列不讲 ArkTS 基础语法与环境搭建假设你已跟完第 1–8 篇。一、场景拆解游戏页 Stack 根布局打开entry/src/main/ets/pages/Index.ets的build()// 来源entry/src/main/ets/pages/Index.ets build() build() { Stack() { if (this.gameState GameState.IDLE) { MainMenuView() // 主菜单 } else if (this.gameState GameState.PLAYING || this.gameState GameState.PAUSED) { GameView() // 游戏页 if (this.gameState GameState.PAUSED) { PauseOverlay() // 暂停遮罩 } } else if (this.gameState GameState.GAME_OVER) { GameOverView() // 结束页 } } .width(100%).height(100%) }关键观察Stack只有一个子元素当前状态的视图不是叠层示范。真正展示叠层的在GameView// 来源entry/src/main/ets/pages/Index.ets GameView() Stack() { // 第 1 层棋盘背景网格 Column() { ForEach([0, 1, 2, 3, 4], (row: number) { Row() { ForEach([0, 1, 2, 3, 4, 1, 2, 3], (col: number) { Stack() { Text().fontSize(20) } .width(${100 / COLS}%).height(${100 / ROWS}%) .backgroundColor((row col) % 2 0 ? #F0F4F8 : #E6EDF3) }) }.width(100%).height(${100 / ROWS}%) }) }.width(100%).height(100%) // 第 2 层猫咪层绝对定位 Column() { ForEach(this.cats, (cat: Cat) { Text(this.getCatEmoji(cat.level)) .fontSize(30) .position({ x: ${cat.x * 100 / COLS}%, y: ${cat.y * 100 / ROWS}% }) }) }.width(100%).height(100%) // 第 3 层顶部 HUD GameHUD() } .width(100%).height(100%)三层叠结构层内容Z 序定位方式底棋盘背景网格默认最下流式铺满中猫咪层默认中间position绝对定位上顶部 HUD默认最上流式顶部对齐二、Stack 容器核心属性速览Stack是叠层容器子元素自下而上叠后写的在上默认所有子元素居中对齐。2.1 本篇用到的属性属性类型作用本篇取值widthstring | number容器宽度100%heightstring | number容器高度100%alignContentAlignment 枚举子元素对齐位默认CenterzIndexnumber子元素层序0/1/2 控序2.2 Stack vs Row/Column 区别容器子元素排列默认对齐适用Row横排依次VerticalAlign.Center横排布局Column竖排依次HorizontalAlign.Center竖排布局Stack叠层Z 轴Alignment.Center叠层、覆盖、绝对定位记忆Row/Column是「排」Stack是「叠」——排是二维布局叠是 Z 轴层序。三、alignContent 9 位对齐Alignment枚举 9 值3×3 网格枚举水平垂直说明TopStart左顶左上角Top中顶顶部居中TopEnd右顶右上角Start左中左居中Center中中居中默认End右中右居中BottomStart左底左下角Bottom中底底部居中BottomEnd右底右下角3.1 游戏页用默认 CenterStack() { Column() { /* 棋盘 */ }.width(100%).height(100%) Column() { /* 猫咪 */ }.width(100%).height(100%) GameHUD() } .width(100%).height(100%) // alignContent 不写默认 Center棋盘和猫咪层都width(100%).height(100%)鲺满alignContent无所谓——满铺就对齐。HUD 不铺满默认居中——但 HUD 要顶部对齐所以实战中要改Stack() { Column() { /* 棋盘 */ }.width(100%).height(100%) Column() { /* 猫咪 */ }.width(100%).height(100%) GameHUD() } .width(100%).height(100%) .alignContent(Alignment.Top) // HUD 顶部居中3.2 全屏遮罩用 CenterStack() { GameView() // 游戏页 PauseOverlay() // 暂停遮罩 } .alignContent(Alignment.Center) // 遮罩居中盖PauseOverlay是个半透明黑色Column居中盖在GameView上。四、zIndex 显式控层序默认 Stack 子元素后写的在上。要打破书写顺序用zIndexStack() { Column() { /* 棋盘背景 */ }.width(100%).height(100%).zIndex(0) Column() { /* 猫咪层 */ }.width(100%).height(100%).zIndex(1) GameHUD().zIndex(2) PauseOverlay().zIndex(3) // 暂停遮罩最上 }zIndex 规则数值越大越在上0 最底3 最顶。不设默认 0同 0 时后写在上。负值合法zIndex(-1)沉到所有默认 0 之下。何时显式设 zIndex状态遮罩必须最上zIndex(10)。HUD 常驻上层zIndex(5)。背景层沉底zIndex(0)或不设。提示别滥用 zIndex。多数场景靠书写顺序后写在上就够显式设 zIndex 是为了「后写但要在下」的反序情况。五、position 绝对定位猫咪层的猫咪不靠流式布局靠position绝对定位到棋盘格Text(this.getCatEmoji(cat.level)) .fontSize(30) .position({ x: ${cat.x * 100 / COLS}%, y: ${cat.y * 100 / ROWS}% })5.1 position 接受对象.position({ x: number | string, y: number | string })x距左上角水平偏移支持 vp 数字或百分比字符串。y距左上角垂直偏移同上。关键position是相对于父容器左上角父容器必须是Stack或定位容器Column/Row里用position会脱离流式布局但定位基准仍是父左上角。5.2 百分比定位的响应式.position({ x: ${cat.x * 100 / COLS}%, y: ${cat.y * 100 / ROWS}% })猫咪cat.x 2列数COLS 5则x 40%——无论屏宽多少猫咪都落在第 3 列40% 处。百分比定位是响应式绝对定位的甜区。5.3 vp 定位的固定尺寸.position({ x: 100, y: 200 }) // 距左 100vp距顶 200vp固定 vp 定位不响应屏宽适合固定尺寸棋盘如棋盘格子固定 60vp。六、实战 1三层叠游戏页Stack() { // 第 1 层棋盘背景 Column() { ForEach([0, 1, 2, 3, 4], (row: number) { Row() { ForEach([0, 1, 2, 3, 4, 1, 2, 3], (col: number) { Stack() { Text().fontSize(20) } .width(${100 / COLS}%).height(${100 / ROWS}%) .backgroundColor((row col) % 2 0 ? #F0F4F8 : #E6EDF3) }) }.width(100%).height(${100 / ROWS}%) }) }.width(100%).height(100%).zIndex(0) // 第 2 层猫咪层绝对定位 Column() { ForEach(this.cats, (cat: Cat) { Text(this.getCatEmoji(cat.level)) .fontSize(30) .position({ x: ${cat.x * 100 / COLS}%, y: ${cat.y * 100 / ROWS}% }) }) }.width(100%).height(100%).zIndex(1) // 第 3 层顶部 HUD GameHUD().zIndex(2) } .width(100%).height(100%) .alignContent(Alignment.Top)七、实战 2全屏暂停遮罩Builder PauseOverlay() { Column() { Text(游戏暂停).fontSize(32).fontWeight(FontWeight.Bold).fontColor(#FFFFFF).margin({ bottom: 32 }) Button(继续游戏) .width(70%).height(56).fontSize(18).fontColor(#FFFFFF) .backgroundColor(#2ECC71).borderRadius(28) .margin({ bottom: 16 }) .onClick(() { this.gameState GameState.PLAYING; }) Button(重新开始) .width(70%).height(56).fontSize(18).fontColor(#FFFFFF) .backgroundColor(#E74C3C).borderRadius(28) .onClick(() { this.clearTimers(); this.startGame(); }) } .width(100%).height(100%) .backgroundColor(rgba(0,0,0,0.7)) // 半透明黑遮罩 .alignItems(HorizontalAlign.Center) .justifyContent(FlexAlign.Center) } // 使用叠在 GameView 上 Stack() { GameView() if (this.gameState GameState.PAUSED) { PauseOverlay() } }遮罩三要点width(100%).height(100%)齔屏遮盖。rgba(0,0,0,0.7)半透明黑让下层游戏隐约可见。justifyContent(Center) alignItems(Center)遮罩内部按钮居中。八、实战 3居中浮弹窗Stack() { GameView() // 居中浮弹窗非全屏遮罩 Column() { Text(确认重新开始).fontSize(18).fontColor(#2C3E50).margin({ bottom: 24 }) Row({ space: 12 }) { Button(取消).width(100).height(44).backgroundColor(#95A5A6).fontColor(#FFFFFF).borderRadius(22) Button(确认).width(100).height(44).backgroundColor(#E74C3C).fontColor(#FFFFFF).borderRadius(22) } } .width(70%).padding(24) .backgroundColor(#FFFFFF).borderRadius(16) .shadow({ radius: 16, color: rgba(0,0,0,0.3), offsetY: 4 }) } .alignContent(Alignment.Center) // 弹窗居中弹窗 vs 遮罩差异维度全屏遮罩居中弹窗宽高100%/100%70%/auto底色rgba(0,0,0,0.7)#FFFFFF对齐内部居中容器居中alignContent阴影无有浮起感九、调试技巧叠层怎么量加临时 border给每层加不同色 border红/蓝/绿看清层叠边界。zIndex验层序遮罩没盖住游戏查 zIndex 是否比游戏大。position验基准猫咪定位偏父容器是否Stackposition基准是否父左上角。真机看遮罩透明预览器渲染接近真机但rgba透明度可能差异以真机为准。十、性能与最佳实践Stack层数别超 5每层都是独立子树超过 5 层布局计算开销显著。zIndex别跳数用 0/1/2/3 连续别 0/10/100/1000 跳——视觉无差异还难维护。position配Stack绝对定位在Stack里最自然在Column/Row里会脱离流式但基准仍是父。遮罩rgba透明度 0.6–0.8低于 0.6 下层太清晰不像遮罩高于 0.8 太黑看不见下层。总结本篇我们从游戏页Stack根布局切入掌握Stack的alignContent9 位、zIndex层序、position绝对定位三大要点并给出了三层游戏页、全屏暂停遮罩、居中浮弹窗三种叠层布局的完整代码。核心要点Stack 是叠非排后写在上 zIndex 破序position 配 Stack 做绝对定位遮罩 100% rgba 0.7。下一篇我们将继续主菜单拆解if/else状态驱动的页面切换机制。如果这篇文章对你有帮助欢迎点赞、收藏⭐、关注你的支持是我持续创作的动力相关资源「猫猫大作战」项目源码本仓库entry/src/main/ets/pages/Index.etsStack 容器官方指南Stack API 参考Alignment 枚举开源鸿蒙跨平台社区HarmonyOS 开发者官方文档首页系列索引本仓库articles/INDEX.md