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 对齐方式
对齐用 justifyContent 和 alignItems 控制。前者管主轴,后者管交叉轴。
| 属性 | 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 布局对齐与间距总结
说了这么多,其实核心就三点:
- 选对容器:一维排列用 Row/Column,需要换行用 Flex,需要叠加用 Stack
- 对齐要明确:主轴用 justifyContent,交叉轴用 alignItems
- 间距要统一:能用 space 参数就别用 margin,保持代码一致性
我的经验:刚开始学布局时,我总喜欢嵌套很多层。后来发现,能用一层解决的绝不用两层。每多一层嵌套,代码可读性就下降一分,性能也受影响。
3.5 知识体系图
下面这张图帮你理清今天讲的内容:
小技巧:如果你不确定用哪个布局,先问自己三个问题:
- 子组件是排成一行还是一列?→ Row 或 Column
- 需要换行吗?→ 需要就用 Flex
- 需要叠加吗?→ 需要就用 Stack
好了,布局基础就讲到这里。记住,布局没有银弹,多写多练自然就熟练了。下一节咱们聊聊更高级的布局技巧。
公众号:蓝海资料掘金营,微信deep3321