3、布局基础:线性布局(Row/Column)、Flex布局、层叠布局(Stack)、布局对齐与间距

布局,说白了就是决定「控件往哪放」。

我刚开始接触鸿蒙开发时,觉得布局不就是拖拖控件嘛。后来发现,真正决定界面好不好看、适不适配的,恰恰是这些最基础的布局组件。今天咱们就把 Row、Column、Flex、Stack 这四个核心布局讲透。

3.1 线性布局:Row 与 Column

线性布局是最常用的布局方式。Row 是水平排列,Column 是垂直排列。我个人习惯把它们叫做「一维布局」—— 所有子组件都沿着一条线排开。

核心概念:主轴与交叉轴

Row 的主轴是水平方向,交叉轴是垂直方向。
Column 的主轴是垂直方向,交叉轴是水平方向。

来看一个最简单的例子:

Row() {
  Text('左')
  Text('中')
  Text('右')
}
.width('100%')
.height(50)

嗯,这里要注意:如果不设置宽度,Row 会默认包裹内容。我建议在大多数场景下显式设置宽度,避免布局跑偏。

3.1.1 对齐方式

对齐用 justifyContentalignItems 控制。前者管主轴,后者管交叉轴。

属性 Row 效果 Column 效果
justifyContent(FlexAlign.Start) 靠左 靠上
justifyContent(FlexAlign.Center) 水平居中 垂直居中
justifyContent(FlexAlign.End) 靠右 靠下
justifyContent(FlexAlign.SpaceBetween) 两端对齐,中间等距 同上(垂直方向)

我在项目中遇到过一个问题:两个按钮想要一个靠左、一个靠右。用 SpaceBetween 就搞定了,比用两个 Row 嵌套省事得多。

3.1.2 间距控制

间距有两种方式:

  • space 属性:Row 和 Column 直接支持 space 参数,设置子元素之间的间距
  • margin/padding:给每个子组件单独设置外边距
Row({ space: 12 }) {
  Button('按钮1')
  Button('按钮2')
  Button('按钮3')
}

我的习惯:能用 space 就用 space,代码更干净。只有需要个别元素特殊间距时,才用 margin。

3.2 Flex 布局

Flex 布局比 Row/Column 更灵活。说白了,Row 和 Column 是 Flex 的特例。Flex 可以自由切换主轴方向,还能控制换行。

为什么需要 Flex?你想想看,如果一行有 5 个标签,屏幕宽度不够怎么办?Row 会挤在一起,Flex 可以自动换行。

Flex({
  direction: FlexDirection.Row,
  wrap: FlexWrap.Wrap,
  justifyContent: FlexAlign.Start
}) {
  ForEach(this.tags, (tag: string) => {
    Text(tag)
      .padding(8)
      .backgroundColor('#f0f0f0')
      .margin(4)
  })
}

我曾经做过一个标签选择器,用户可能选 3 个,也可能选 20 个。用 Flex 加换行,完美适配。如果用 Row,就得写一堆判断逻辑。

3.2.1 Flex 的核心参数

参数 可选值 说明
direction Row / Column / RowReverse / ColumnReverse 主轴方向
wrap NoWrap / Wrap / WrapReverse 是否换行
justifyContent Start / Center / End / SpaceBetween / SpaceAround / SpaceEvenly 主轴对齐
alignItems Start / Center / End / Stretch / Baseline 交叉轴对齐

避坑指南:我曾经把 wrap 设成 NoWrap,结果标签全挤在一行,文字都重叠了。后来排查半天才发现是 wrap 的问题。记住:内容多、宽度不确定时,一定要用 Wrap。

3.3 层叠布局:Stack

Stack 允许子组件叠在一起。就像叠扑克牌一样,后面的盖在前面的上面。

什么时候用 Stack?最常见的场景是:

  • 图片上加文字(比如头像上的在线状态)
  • 遮罩层、弹窗
  • 徽标(Badge)效果
Stack() {
  Image($r('app.media.avatar'))
    .width(100)
    .height(100)
    .borderRadius(50)
  
  Text('在线')
    .fontSize(12)
    .backgroundColor('#52c41a')
    .padding({ left: 6, right: 6 })
    .borderRadius(10)
    .position({ x: 70, y: 70 })
}

嗯,这里要注意 position 属性。Stack 的子组件默认都从左上角开始排列。用 position 可以精确控制每个子组件的位置。

3.3.1 Stack 的对齐方式

Stack 也有 alignContent 属性,控制所有子组件的默认对齐方式:

  • TopStart:左上角(默认)
  • Center:居中
  • BottomEnd:右下角
  • 等等,共 9 个位置

我个人习惯:如果子组件需要精确控制位置,用 position;如果所有子组件都居中,用 alignContent(Center) 更省事。

3.4 布局对齐与间距总结

说了这么多,其实核心就三点:

  1. 选对容器:一维排列用 Row/Column,需要换行用 Flex,需要叠加用 Stack
  2. 对齐要明确:主轴用 justifyContent,交叉轴用 alignItems
  3. 间距要统一:能用 space 参数就别用 margin,保持代码一致性

我的经验:刚开始学布局时,我总喜欢嵌套很多层。后来发现,能用一层解决的绝不用两层。每多一层嵌套,代码可读性就下降一分,性能也受影响。

3.5 知识体系图

下面这张图帮你理清今天讲的内容:

布局基础核心体系 Row / Column Flex 布局 Stack 布局 对齐与间距 主轴 / 交叉轴 justifyContent / alignItems space 间距参数 direction 方向 wrap 换行控制 SpaceBetween / SpaceAround 层叠顺序 position 精确定位 alignContent 对齐 主轴对齐 交叉轴对齐 margin / padding 核心原则:选对容器 → 明确对齐 → 统一间距 减少嵌套,保持代码简洁

小技巧:如果你不确定用哪个布局,先问自己三个问题:

  • 子组件是排成一行还是一列?→ Row 或 Column
  • 需要换行吗?→ 需要就用 Flex
  • 需要叠加吗?→ 需要就用 Stack

好了,布局基础就讲到这里。记住,布局没有银弹,多写多练自然就熟练了。下一节咱们聊聊更高级的布局技巧。


公众号:蓝海资料掘金营,微信deep3321