【学习目标】
- 理解Stack叠层布局的核心定位与能力,掌握其构造参数、层叠控制规则;
- 掌握Alignment枚举9个取值及对应对齐效果,熟练使用zIndex实现组件层级手动控制;
- 掌握offset轻量偏移、position绝对定位的语法与使用规则,实现Stack布局精准层叠定位;
- 通过基础示例落地核心知识点,结合移动端高频实战案例,实现从Stack基础到业务开发的转化。
一、Stack叠层布局核心基础
1.1 核心定位与适用场景
Stack是鸿蒙ArkTS中实现组件Z轴层叠排列的基础布局容器,核心能力为让多个子组件在同一空间区域内上下叠加显示,支持子组件的统一对齐和层级灵活控制。
适用于消息角标、商品卡片、悬浮按钮、层叠遮罩、图标叠加等多组件共用同一空间且需要视觉层叠的开发场景,无需嵌套复杂线性布局即可实现空间复用。
1.2 核心构造参数
Stack布局仅提供一个构造参数,用于统一设置容器内所有子组件的默认对齐位置,语法如下:- Stack(value?: { alignContent?: Alignment })
复制代码 1.3 子组件全局对齐(Alignment)
- 作用:设置Stack容器内所有子组件的默认对齐方位,决定子组件在容器内的初始布局位置;
- 默认值:Alignment.Center(所有子组件默认在容器正中对齐);
- 取值:鸿蒙标准Alignment枚举9个选项,覆盖所有对齐场景:
- 顶部系列:TopStart(左上)、Top(上中)、TopEnd(右上)
- 中部系列:Start(左中)、Center(正中)、End(右中)
- 底部系列:BottomStart(左下)、Bottom(下中)、BottomEnd(右下)
- 基础使用示例:
- Stack({ alignContent: Alignment.Start }) {
- Text("左中对齐子组件1")
- .backgroundColor("#E5F2FF")
- .padding({ top: 8, right: 8, bottom: 8, left: 8 });
- Text("左中对齐子组件2")
- .backgroundColor("#FFE5E5")
- .padding({ top: 8, right: 8, bottom: 8, left: 8 });
- }
- .width(300)
- .height(200)
- .backgroundColor("#F5F5F5")
复制代码 1.4 层叠顺序控制规则
层叠顺序是Stack布局的核心能力,所有规则仅在当前Stack容器内生效,不影响其他容器/页面:
- 默认层叠规则:遵循后写覆盖先写原则,后编写的子组件默认显示在更上层,会覆盖先编写的子组件(若组件存在重叠区域);
- 手动层叠规则(zIndex属性):突破编写顺序限制,实现自定义层级,适用于复杂层叠场景:
- 取值范围:支持正整数、负整数、0;
- 核心原则:数值越大层级越高,显示在越上层;数值越小层级越低,显示在越下层;
- 同值规则:多个子组件zIndex相同时,回归“后写覆盖先写”的默认规则;
- 层叠控制示例:
- Stack({ alignContent: Alignment.Center }) {
- Text("先写组件\n(zIndex=1)")
- .width(180)
- .height(180)
- .backgroundColor("#FFE5E5")
- .zIndex(1); // 层级更高,显示在上方
- Text("后写组件\n(zIndex=0)")
- .width(120)
- .height(120)
- .backgroundColor("#80BFFF")
- .zIndex(0); // 层级更低,显示在下方
- }
- .width(240)
- .height(240)
- .backgroundColor("#F5F5F5")
复制代码 1.5 偏移能力offset(相对定位)
纯视觉位置调整,基于自身原始位置偏移,不改变组件布局属性,不影响布局流,适用于细节贴边、位置校准:
- .offset({ x: Length, y: Length })
- // x:水平偏移,正数右移、负数左移;y:垂直偏移,正数下移、负数上移
复制代码
- 核心规则:
- 仅调整视觉显示位置,组件原有布局占位、兄弟组件位置均不变;
- 偏移基准为组件自身的默认布局位置(由Stack对齐规则决定);
- 交互区域仍保留在原始布局位置,不跟随视觉偏移;
- 适合小范围微调,如消息角标贴边、文字与图标对齐校准等场景。
1.6 绝对定位position
脱离布局流的悬浮定位,以直接父容器Stack为基准,适用于悬浮按钮、标签、角标等场景:
- .position({
- left?: Length, // 距父容器左侧边界偏移
- top?: Length, // 距父容器顶部边界偏移
- right?: Length, // 距父容器右侧边界偏移
- bottom?: Length // 距父容器底部边界偏移
- })
复制代码
- 核心规则:
- 脱离布局流,不占用任何布局空间,不影响其他组件的布局和排列;
- 定位基准为直接父容器Stack的物理边界,父容器必须显式设置固定宽高/明确尺寸,否则定位基准丢失,出现偏移/失效;
- 默认层级高于未定位组件,可通过zIndex调整层级;
- 支持同时设置多个方向偏移(如top:10+right:10),实现精准悬浮定位;
- 交互区域跟随视觉定位位置,与偏移后的显示位置一致。
1.7 offset与position核心差异
对比维度offset 轻量偏移(相对定位)position 绝对定位布局流影响无影响,仅视觉调整,保留原占位完全脱离布局流,无原始占位定位基准组件自身默认布局位置直接父容器Stack物理边界父容器要求无强制宽高要求,自适应即可必须显式设置固定宽高/明确尺寸调整范围小范围视觉微调,贴合原位置全容器自由定位,无位置限制交互区域位置保留在原始布局位置跟随视觉偏移位置性能影响无额外性能消耗少量性能消耗,可忽略适用场景角标贴边、位置校准、细节微调悬浮按钮、热销标签、固定角标简单解释:offset只是修改了绘制显示位置,实际组件坐标并未修改。position修改了显示位置,同时坐标也不再是原坐标。
二、工程说明
2.1 工程结构
本节创建新工程 StackApplication(基于鸿蒙 API 12+ / Stage 模型),用于编写和运行叠层布局(Stack)相关演示代码,工程核心目录结构如下:- StackApplication/
- ├── AppScope/
- ├── entry/
- │ ├── src/
- │ │ ├── main/
- │ │ │ ├── ets/
- │ │ │ │ ├── entryability/
- │ │ │ │ │ └── EntryAbility.ets
- │ │ │ │ └── pages/
- │ │ │ │ ├── Index.ets # 入口导航页面
- │ │ │ │ ├── StackBasicPage.ets # 基础能力示例页
- │ │ │ │ ├── AppIconBadgePage.ets # 实战案例1:消息角标
- │ │ │ │ └── ProductCardPage.ets # 实战案例2:商品卡片
- │ │ │ ├── resources/
- │ │ │ │ └── base/
- │ │ │ │ └── media/ # 图片资源目录
- │ │ │ └── module.json5
- │ └── build-profile.json5
- └── build-profile.json5
复制代码 2.2 资源准备
需将图片资源放入 entry/src/main/resources/base/media 目录:
- startIcon.png:App图标资源(用于消息角标案例)
- huaweiTaplet.png:华为平板商品图片(用于商品卡片案例)
三、入口导航页面:Index.ets
- import { router } from '@kit.ArkUI';
- @Entry
- @Component
- struct Index {
- build() {
- Column({ space: 20 }) {
- // 页面标题
- Text("Stack叠层布局实战教程")
- .fontSize(28)
- .fontWeight(FontWeight.Bold)
- .width('100%')
- .textAlign(TextAlign.Center)
- .margin({ bottom: 40 });
- // 导航按钮1:Stack基础能力演示
- Button("1. Stack基础能力演示")
- .fontSize(18)
- .fontWeight(FontWeight.Medium)
- .fontColor($r('sys.color.white'))
- .backgroundColor('#1890FF')
- .padding({ top: 12, right: 40, bottom: 12, left: 40 })
- .borderRadius(8)
- .onClick(() => {
- router.pushUrl({ url: 'pages/StackBasicPage' });
- })
- // 导航按钮2:实战案例-App图标+消息角标
- Button("2. 实战案例:App图标+消息角标")
- .fontSize(18)
- .fontWeight(FontWeight.Medium)
- .fontColor($r('sys.color.white'))
- .backgroundColor('#13C2C2')
- .padding({ top: 12, right: 40, bottom: 12, left: 40 })
- .borderRadius(8)
- .onClick(() => {
- router.pushUrl({ url: 'pages/AppIconBadgePage' });
- })
- // 导航按钮3:实战案例-商品销售卡片
- Button("3. 实战案例:商品销售卡片")
- .fontSize(18)
- .fontWeight(FontWeight.Medium)
- .fontColor($r('sys.color.white'))
- .backgroundColor('#722ED1')
- .padding({ top: 12, right: 40, bottom: 12, left: 40 })
- .borderRadius(8)
- .onClick(() => {
- router.pushUrl({ url: 'pages/ProductCardPage' });
- })
- }
- .width('100%')
- .height('100%')
- .padding(20)
- .justifyContent(FlexAlign.Center)
- .alignItems(HorizontalAlign.Center)
- .backgroundColor('#F5F7FA');
- }
- }
复制代码 运行效果
四、基础示例演示:StackBasicPage.ets
- @Entry
- @Component
- struct StackBasicPage {
- build() {
- Column({ space: 40 }) {
- // 页面标题
- Text("Stack核心基础能力演示")
- .fontSize(24)
- .fontWeight(FontWeight.Bold)
- .width('100%')
- .textAlign(TextAlign.Center);
-
- Text("示例1:叠层布局对齐方式: Alignment.TopStart")
- .fontSize(16)
- .fontWeight(FontWeight.Medium)
- .width('100%')
- .textAlign(TextAlign.Start);
- StackComponent()
- Text("示例2:Z序控制演示(zIndex调整层级)")
- .fontSize(16)
- .fontWeight(FontWeight.Medium)
- .width('100%')
- .textAlign(TextAlign.Start);
- StackComponent({z:1})
- }
- .width('100%')
- .height('100%')
- .padding(20)
- .justifyContent(FlexAlign.Center)
- .backgroundColor("#F8F9FA");
- }
- }
- // 自定义可复用Stack组件
- @Component
- struct StackComponent{
- @Prop z: number = 0
- build(){
- Stack({ alignContent: Alignment.TopStart }) {
- Text("元素1")
- .backgroundColor("#80BFFF")
- .borderRadius(4)
- .width('80%')
- .height('80%')
- .zIndex(this.z)
- .textAlign(TextAlign.End)
- .fontSize(14);
- Text("元素2")
- .backgroundColor("#E5F2FF")
- .borderRadius(4)
- .textAlign(TextAlign.Center)
- .width('50%')
- .height('50%')
- .fontSize(14);
- }
- .width(240)
- .height(180)
- .backgroundColor(Color.Pink)
- .borderRadius(8)
- }
- }
复制代码 运行效果
五、实战案例一:App图标+消息角标(AppIconBadgePage.ets)
- @Entry
- @Component
- struct AppIconBadgePage {
- build() {
- Column({ space: 30 }) {
- Text("App图标 + 消息角标")
- .fontSize(22)
- .fontWeight(FontWeight.Bold);
- // 核心:Stack层叠 + Alignment对齐 + offset微调 实现消息角标效果
- Stack({ alignContent: Alignment.TopEnd }) {
- // 底层:App图标(基础元素)
- Image($r('app.media.startIcon'))
- .width(80)
- .height(80)
- .onAreaChange((oldValue: Area, newValue: Area) => {
- const x = newValue.position.x as number; // 直接获取vp单位坐标
- const y = newValue.position.y as number;
- console.log(`Imagex轴:${x},y轴:${y}`)
- })
- .borderRadius(16);
- // 上层:消息角标(offset负偏移实现精准贴边)
- Text("5")
- .width(24)
- .height(24)
- .backgroundColor("#FF3B30")
- .fontColor(Color.White)
- .fontSize(12)
- .textAlign(TextAlign.Center)
- .borderRadius(12)
- .onAreaChange((oldValue: Area, newValue: Area) => {
- const x = newValue.position.x as number; // 直接获取vp单位坐标
- const y = newValue.position.y as number;
- console.log(`Textx轴:${x},y轴:${y}`)
- })
- .offset({ x: 6, y: -6 });
- }
- }
- .width('100%')
- .height('100%')
- .padding(20)
- .justifyContent(FlexAlign.Center)
- .backgroundColor("#F8F9FA");
- }
- }
复制代码 5.1 运行效果
5.2 offset的总结验证
日志显示 Imagex轴:0,y轴:0,Textx轴:56,y轴:0;
Image宽高80vp,即Stack宽高80vp ,消息角标右上角对齐;Text组件宽高24vp;
Text相对于Stack起始坐标 80 - 24 = 56 符合日志输出56。
设置Text.offset({ x: 6, y: -6 }); 和日志对比 y坐标0可以看出修改的只是绘制显示位置,实际坐标未变化。
六、实战案例二:商品销售卡片(ProductCardPage.ets)
- @Entry
- @Component
- struct ProductCardPage {
- build() {
- Column({ space: 30 }) {
- Text("商品销售卡片")
- .fontSize(22)
- .fontWeight(FontWeight.Bold);
- // 核心:Stack+position 实现复杂多层级商品卡片底部居中对齐
- Stack({ alignContent: Alignment.Bottom }) {
- // 底层:商品背景图(铺满容器,保持比例无变形)
- Image($r('app.media.huaweiTaplet'))
- .width('100%')
- .height('100%')
- .objectFit(ImageFit.Cover);
- // 中层:价格半透明遮罩(底部居中对齐,原生支持无需额外调整)
- Text("¥3299 限时特价")
- .width("100%")
- .padding({ top: 8, bottom: 8})
- .backgroundColor("rgba(0, 0, 0, 0.5)")
- .fontColor(Color.White)
- .fontSize(14)
- .textAlign(TextAlign.Center);
- // 上层:热销标签(position绝对定位,悬浮右上角,脱离布局流)
- Text("热销")
- .padding({ top: 4, right: 10, bottom: 4, left: 10 })
- .backgroundColor("#FF3B30")
- .fontColor(Color.White)
- .fontSize(12)
- .borderRadius(4)
- .position({ top: 10, right: 10 });
- }
- .width("100%")
- .aspectRatio(4/3) // 4:3宽高比,明确容器尺寸(position定位核心前提)
- .borderRadius(12)
- }
- .width("100%")
- .height("100%")
- .padding(20)
- .justifyContent(FlexAlign.Center)
- .backgroundColor("#F8F9FA");
- }
- }
复制代码 6.1 核心知识点落地
- 明确尺寸基准:Stack通过width("100%")+aspectRatio(4/3)设置明确的宽高比例尺寸,为position绝对定位的热销标签提供精准物理边界,保证定位无偏移(position使用的核心前提);
- 多层级自然覆盖:按“背景图→价格遮罩→热销标签”的编写顺序,实现符合视觉逻辑的层级覆盖,满足电商卡片“背景-内容-标识”的设计要求,无需额外设置zIndex;
- 底部对齐原生支持:通过Stack的alignContent: Alignment.Bottom直接实现价格遮罩底部居中对齐,无需额外的alignSelf和offset微调,简化代码;
- position悬浮定位:热销标签通过position绝对定位脱离布局流,以Stack为基准通过top:10、right:10实现精准悬浮,不影响其他组件的布局和层叠,交互区域跟随悬浮位置;
- 图片适配优化:为图片添加width('100%')+height('100%'),配合objectFit(ImageFit.Cover)保证商品图片在固定比例尺寸内不变形、不拉伸,自动裁剪中心核心区域,适配不同比例的商品图片;
- 半透明遮罩规范:价格遮罩使用rgba(0,0,0,0.5)半透明背景,既保证文字可读性,又不遮挡底层商品图片的细节,符合电商UI设计规范。
6.2 运行效果
七、内容总结
- 核心定位:Z轴层叠布局容器,核心能力是多组件同一空间层叠显示,是移动端角标、卡片、悬浮元素的必备布局;
- 唯一构造参数:alignContent,控制子组件全局对齐,取值为Alignment枚举9个选项,默认Center;
- 层叠两大规则:默认“后写覆盖先写”,手动通过zIndex控制(数值越大层级越高),规则仅在当前Stack内生效;
- 定位两大能力:
- offset:相对定位,纯视觉微调,不影响布局流,保留原占位和原始交互区域,偏移基准为组件自身默认位置,适用于贴边、校准(如消息角标);
- position:绝对定位,脱离布局流无占位,交互区域跟随视觉位置,定位基准为直接父容器Stack,使用前提是父容器设置固定宽高/明确尺寸,适用于悬浮定位(如热销标签);
- 实战组合技巧:
- Stack+Alignment+offset:适合“层叠+细节贴边”场景(如消息角标),不改变布局流,适配性强;
- Stack+明确尺寸+position:适合“层叠+悬浮定位”场景(如商品卡片热销标签),精准锚定父容器位置;
- 三者组合:可实现复杂多层级的移动端常见布局(如商品卡片、弹窗、悬浮按钮等);
- 核心避坑点:position绝对定位必须保证父容器有明确尺寸,否则会定位失效;offset偏移后交互区域仍在原始位置,避免因视觉偏移导致交互异常。
八、代码仓库
- 工程名称:StackApplication
- 仓库地址:https://gitee.com/juhetianxia321/harmony-os-code-base.git
九、下节预告
本节我们系统学习了 Stack 叠层布局 的核心用法,掌握了 Z 轴层叠、Alignment 对齐、zIndex 层级控制、offset 相对偏移与 position 绝对定位五大关键能力,并通过消息角标、商品卡片两大实战案例,完成了从基础语法到移动端高频场景的落地转化。
下一节,我们将进入鸿蒙 ArkTS 中更适合复杂界面、扁平化开发的高级布局 ——RelativeContainer 相对布局。它可以让组件直接以父容器或兄弟组件为锚点进行精准定位,告别传统 Column/Row 多层嵌套,实现更灵活、更简洁、更易维护的页面结构。
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |