鸿蒙原生 ArkTS 布局方式入门:Stack 堆叠布局 — 层叠式排列的核心概念


在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

一、引言

在鸿蒙原生应用开发中,布局是 UI 最核心的能力。ArkUI 的 ColumnRowFlexRelativeContainer 各有所长,而本文的主角 Stack(堆叠布局) 是其中唯一天然支持 Z 轴层叠 的容器。

Stack 在 Android 对应 FrameLayout,在 Flutter 对应 Stack,在 Web 对应 position: absolute + relative。它的核心思想极简:子组件按添加顺序,从下往上依次叠放——先添加的在底层(Z 值小),后添加的在上层(Z 值大)。

应用场景非常广泛:头像右上角叠加未读红点、商品图片左上角显示"热卖"标签、卡片底部叠加半透明遮罩、多个头像部分重叠形成群组、页面顶层覆盖加载动画屏蔽层。没有掌握 Stack,就不算真正掌握了鸿蒙 UI 开发。


二、Stack 布局核心概念

2.1 什么是 Stack

Stack 是 ArkUI 内置容器组件,无需 import,可直接使用。它让子组件在 Z 轴方向上层叠排列。与 Column(纵向)和 Row(横向)不同,Stack 的所有子组件占据同一平面空间,通过前后叠放构建 UI 层次。

布局容器 排列方向 生活类比
Column Y 轴(垂直) 书架上的书,从上到下
Row X 轴(水平) 餐桌上的餐具,从左到右
Stack Z 轴(深度) 桌面上叠放的纸张

2.2 Z 轴层叠机制

Stack 的规则极其简单:先添加的在底层,后添加的在上层

     Z 轴方向(越往上越靠前)
          ↑
    ┌────────────────────┐  ← 第3层:Text(最上层)
    │   Z=3(顶层)       │
    └────────────────────┘
    ┌────────────────────┐  ← 第2层:绿色矩形(中层)
    │   Z=2(中层)       │
    └────────────────────┘
    ┌────────────────────┐  ← 第1层:蓝色矩形(底层)
    │   Z=1(底层)       │
    └────────────────────┘

这个顺序不可逆——无法在运行时动态调整某个子项的 Z 轴顺序(除非重新排列代码中的添加顺序)。因此编码时建议按"底层 → 中层 → 顶层"顺序编写子组件

2.3 API 24 的注意事项

  • 对齐枚举统一为 AlignmentStackAlign 已废弃,使用 Alignment 枚举
  • 性能优化:子组件超过 10 个时布局计算性能提升约 30%
  • 嵌套稳定性:多层 Stack 嵌套的尺寸溢出问题已修复

三、Stack 基本用法

3.1 最简单的 Stack

@Entry
@Component
struct SimpleStack {
  build() {
    Stack() {
      // 子组件在此层叠
    }
    .width('100%')
    .height('100%')
  }
}

Stack 是 ArkUI 的全局内置类型,无需 import 声明。

3.2 alignContent 参数

构造函数接受可选的 alignContent,控制所有子组件的默认对齐方式

Stack({ alignContent: Alignment.Center }) {
  // 所有子组件默认居中对齐
}

不指定时默认值为 Alignment.CenterAlignment 共 9 个取值:

对齐值 说明
TopStart / Top / TopEnd 左上 / 顶部居中 / 右上
Start / Center / End 左中 / 居中 / 右中
BottomStart / Bottom / BottomEnd 左下 / 底部居中 / 右下

3.3 尺寸行为要点

  • 无子组件时 Stack 尺寸为 0×0
  • 子组件全部固定尺寸时,Stack 自适应到最大子组件的尺寸
  • 子组件使用百分比(如 width('100%'))时,Stack 必须有固定尺寸
  • 最佳实践:始终为 Stack 显式设置 widthheight

四、alignContent 动态切换

使用 @State 动态切换 9 种对齐方式,直观感受每种模式的效果:

@State currentAlignIndex: number = 0;
private alignOptions: Alignment[] = [
  Alignment.TopStart, Alignment.Top, Alignment.TopEnd,
  Alignment.Start, Alignment.Center, Alignment.End,
  Alignment.BottomStart, Alignment.Bottom, Alignment.BottomEnd,
];

Stack({ alignContent: this.alignOptions[this.currentAlignIndex] }) {
  Rect().fill('#22000000').width('100%').height('100%');
  Rect().width(60).height(60).fill('#FFE74C3C');
  Rect().width(50).height(50).fill('#FF3498DB');
  Rect().width(40).height(40).fill('#FF2ECC71');
}

观察要点TopStart 时三方块"吸"到左上角;Center 时叠在正中间;BottomEnd 时移向右下角。alignContent全局设置,同时影响所有子组件。如需单独控制某个子项,用 .position().margin()


五、实战示例详解

5.1 Z 轴层叠顺序演示

三个不同颜色、尺寸的矩形从下往上叠放,直观展示 Z 轴层级:

Stack({ alignContent: Alignment.Center }) {
  // 第1层(最底层):蓝色最大矩形
  Rect().width(240).height(200).fill('#FF4A90D9')
    .radiusWidth(12);
  // 第2层(中层):绿色中等矩形
  Rect().width(180).height(150).fill('#FF50C878')
    .radiusWidth(12);
  // 第3层(最顶层):白色文字
  Text('Z=3 (最顶层)').fontSize(14).fontColor(Color.White);
}
.width(240).height(200);

效果:三层矩形层层嵌套,蓝色边缘露出约 30px,绿色露出约 15px。底层尺寸决定容器尺寸,中层和顶层只需关注自身尺寸。若三层尺寸相同则完全覆盖,需要用 opacity 才能看到下层。

5.2 角标/徽标叠加(Badge)

这是 Stack 最经典的应用——在图标右上角叠加红色未读数:

Stack({ alignContent: Alignment.TopStart }) {
  // 底层:消息图标
  Circle().width(80).height(80).fill('#FFEEEEEE')
    .stroke('#FFCCCCCC').strokeWidth(2);
  // 信封符号
  Text('✉').fontSize(32).fontColor('#FF666666')
    .alignRules({
      center: { anchor: '__container__', align: VerticalAlign.Center },
      middle: { anchor: '__container__', align: HorizontalAlign.Center }
    });
  // 顶层:红色角标
  Stack({ alignContent: Alignment.Center }) {
    Circle().width(26).height(26).fill('#FFE74C3C');
    Text('3').fontSize(12).fontColor(Color.White)
      .fontWeight(FontWeight.Bold);
  }
  .position({ x: 58, y: -6 });  // ★ 绝对定位到右上角
}
.width(80).height(80);

原理:这是一个二级 Stack 嵌套。外层 Stack 为 TopStart 对齐,内层角标通过 .position({ x: 58, y: -6 }) 偏移到右上角。X 偏移 = 图标宽(80) - 角标宽(26) + 露出量(4) = 58。若需角标在左下角,将外层改为 BottomStart 并调整坐标即可。

5.3 头像堆叠排列(群组场景)

多个头像部分重叠形成群组效果,常用于"xxx 等 N 人赞了":

Stack({ alignContent: Alignment.Start }) {
  // 头像 1 — 最左侧
  Circle().width(48).height(48).fill('#FFE74C3C')
    .stroke(Color.White).strokeWidth(3);
  // 头像 2 — 右移 30px(重叠 18px)
  Circle().width(48).height(48).fill('#FF3498DB')
    .stroke(Color.White).strokeWidth(3).margin({ left: 30 });
  // 头像 3 — 右移 60px
  Circle().width(48).height(48).fill('#FF2ECC71')
    .stroke(Color.White).strokeWidth(3).margin({ left: 60 });
  // +N 提示
  Circle().width(48).height(48).fill('#FF999999')
    .stroke(Color.White).strokeWidth(3).margin({ left: 90 });
  Text('+3').fontSize(14).fontColor(Color.White)
    .fontWeight(FontWeight.Bold).margin({ left: 90 });
}
.width('100%').height(56).padding({ left: 10 });

技巧:使用 Alignment.Start 左对齐,通过递增的 .margin({ left: N }) 让每个头像逐步偏移。重叠量 18px(约头像宽度的 37.5%)在视觉上最舒适。实际项目中可用 ForEach 循环渲染并用 index * overlapOffset 计算偏移量。

5.4 卡片阴影叠加层

通过 Stack 叠加一个偏移的"阴影层",实现比 .shadow() 更丰富的视觉效果:

Stack({ alignContent: Alignment.Center }) {
  // 阴影层:半透明黑色 + 向右下偏移
  Rect().width(260).height(100).fill('#22000000')
    .radiusWidth(12).translate({ x: 4, y: 4 });
  // 主卡片:白色 + 系统阴影
  Rect().width(260).height(100).fill(Color.White)
    .radiusWidth(12)
    .shadow({ radius: 8, color: '#33000000', offsetY: 4 });
  // 卡片内容
  Column({ space: 4 }) {
    Text('卡片标题').fontSize(16).fontWeight(FontWeight.Bold);
    Text('卡片正文内容,展示 Stack 布局的典型应用场景。')
      .fontSize(13).fontColor('#FF888888');
  }
  .alignItems(HorizontalAlign.Start).padding({ left: 16, right: 16 });
}
.width(260).height(100);

两层阴影原理:底层半透明矩形用 translate 偏移 4px 模拟投影,上层主卡片用 shadow 提供柔和边缘阴影。调整 translate 方向即可控制阴影出现在右下、正下或左下。

5.5 加载遮罩层

页面加载时在内容上方显示遮罩 + 加载动画:

Stack() {
  // 底层:页面内容
  Column() { /* 列表、卡片等 */ }
    .width('100%').height('100%');

  // 顶层:遮罩 + 加载动画(通过状态变量控制显示)
  if (this.isLoading) {
    Column() {
      LoadingProgress().width(40).height(40).color(Color.White);
      Text('加载中...').fontSize(14).fontColor(Color.White)
        .margin({ top: 12 });
    }
    .width('100%').height('100%')
    .backgroundColor('#66000000')  // 40% 透明度黑色遮罩
    .justifyContent(FlexAlign.Center)
    .alignItems(HorizontalAlign.Center);
  }
}

要点:用 if 条件渲染控制遮罩显示/隐藏;40% 透明度黑色既能屏蔽操作又能让下层隐约可见。


六、子组件定位方式对比

Stack 提供四种定位方式:

方式 作用域 适用场景
alignContent 全局(所有子组件) 统一对齐,如全部居中
.margin() 单个子组件 头像堆叠等逐个偏移
.position() 单个子组件 角标等精确定位
.alignRules() 单个子组件 锚点对齐,类似 RelativeContainer

七、最佳实践与避坑指南

7.1 适合 vs 不适合

✅ 适合:角标 / 头像堆叠 / 卡片阴影 / 加载遮罩 / 图片标签 / FAB 按钮 / 全屏加载动画

❌ 不适合:列表布局(用 List)/ 简单纵向排列(用 Column)/ 简单横向排列(用 Row)/ 自适应换行(用 FlexWrap)

7.2 常见错误

错误 原因 解决
子组件显示不全 Stack 尺寸太小 显式设置 width/height
百分比尺寸无效 Stack 无固定尺寸 给 Stack 设固定尺寸
角标位置不对 对齐方式不符预期 检查外层 alignContent
子组件全不可见 尺寸均为 0 至少一个子组件设固定尺寸
import Stack 报错 误以为需 import Stack 是内置组件,无需 import

7.3 性能建议

  • 单个 Stack 子组件建议不超过 10 个
  • 嵌套不超过 3 层
  • 非永久显示的组件用 if 条件渲染替代 .visibility()
  • 优先用 .margin() 而不是 .position()

八、Stack 与其他布局对比

特性 Stack Column Row RelativeContainer
排列方向 Z 轴层叠 Y 轴垂直 X 轴水平 锚点相对
子组件重叠 ✅ 是 ❌ 否 ❌ 否 ✅ 是
position 定位 ⚠️ 有限 ⚠️ 有限 ❌ 不支持
alignRules ✅ 原生支持
层叠效果 ✅ 最佳 ⚠️ 可实现

选择建议:90% 场景用 Column + Row;需层叠用 Stack;需精细锚点用 RelativeContainer;需弹性布局用 Flex



九、总结

本文从 Stack 布局的核心——Z 轴层叠机制出发,通过 5 个实战示例,全面讲解了 Stack 在鸿蒙原生 ArkTS 开发中的用法。

核心要点

  1. Z 轴层叠:子组件按添加顺序从下往上叠放,先添加的在底层
  2. 对齐控制alignContent 共 9 种取值(Alignment 枚举),全局控制
  3. 定位方式:全局 alignContent、子级 .margin()、子级 .position()、锚点 alignRules()
  4. 实战场景:角标叠加、头像堆叠、阴影层、遮罩层——覆盖 90% 的 Stack 使用场景
  5. API 24:使用 Alignment 枚举,Stack 为内置组件无需 import

Stack 是 ArkUI 布局体系中"四两拨千斤"的存在——语法极简,应用极广。掌握 Stack,你的鸿蒙应用 UI 表达能力将提升一个台阶。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐