布局体系:Flex/Column/Row/Grid/Stack/RelativeContainer 实战
布局体系:Flex/Column/Row/Grid/Stack/RelativeContainer 实战
界面好不好看、适不适配多端,八成取决于布局。ArkUI 提供从线性到相对定位的完整布局体系。本文逐一拆解每种布局的适用场景与代码,并给出多端适配与响应式布局的实战方案。
一、布局的本质
布局解决三件事:位置、尺寸、对齐。ArkUI 的布局容器(Column/Row/Flex/Grid/Stack/RelativeContainer)决定了子组件的排列规则,而width/height、padding/margin、对齐属性决定细节。理解"谁是主轴、谁是交叉轴"是掌握所有布局的关键。
二、Column 与 Row:线性布局
Column纵向、Row横向,是最基础的线性布局。均基于 Flex 模型。
// Column:主轴=垂直,交叉轴=水平 Column({ space: 12 }) { // space 子项间距 Text('上') Text('下') } .justifyContent(FlexAlign.Center) // 主轴对齐:居中 .alignItems(HorizontalAlign.Start) // 交叉轴对齐:左 .width('100%') .height('100%') .padding(16) // Row:主轴=水平 Row({ space: 8 }) { Text('左') Text('右') } .justifyContent(FlexAlign.SpaceBetween) // 两端分布 .width('100%')主轴用justifyContent,交叉轴用alignItems。这是 Flex 的标准心智模型。
三、Flex:更灵活的方向控制
Flex支持direction、wrap、alignContent:
Flex({ direction: FlexDirection.Row, // 方向 wrap: FlexWrap.Wrap, // 换行 justifyContent: FlexAlign.SpaceAround, alignItems: ItemAlign.Center }) { ForEach(this.tags, (t: string) => { Text(t) .padding({ left: 10, right: 10, top: 4, bottom: 4 }) .backgroundColor('#E8F0FE') .borderRadius(12) }) } .width('100%')FlexWrap.Wrap让标签自动换行,做"标签云"必备。
四、Stack:层叠布局
Stack让子组件重叠摆放,靠alignContent控制对齐,适合"图片上叠加文字/角标":
Stack({ alignContent: Alignment.BottomStart }) { Image($r('app.media.cover')) .width('100%') .height(160) .objectFit(ImageFit.Cover) Text('热门') .fontSize(12) .fontColor('#FFF') .backgroundColor('#FF4D4F') .padding({ left: 8, right: 8, top: 2, bottom: 2 }) .margin({ left: 8, bottom: 8 }) } .width('100%') .borderRadius(12) .clip(true) // 裁剪圆角Stack中子组件按书写顺序叠加,后者在上。zIndex()可手动调层级。
五、Grid:网格布局
Grid+GridItem适合九宫格、相册、卡片墙。可用columnsTemplate定义列宽:
Grid() { ForEach(this.icons, (it: IconItem) => { GridItem() { Column() { Image(it.src).width(40).height(40) Text(it.name).fontSize(12).margin({ top: 4 }) } } }) } .columnsTemplate('1fr 1fr 1fr 1fr') // 4 等列 .rowsGap(12) .columnsGap(12) .width('100%')1fr是"剩余空间等比分配"单位,类似 CSS Grid。GridItem还可通过columnStart/columnEnd跨列,做"大图占两格"的瀑布流。
六、RelativeContainer:相对定位
RelativeContainer让组件以"锚点"相对定位,适合复杂关系布局(如仪表盘)。
RelativeContainer() { Text('标题') .id('title') // 设置锚点 id .alignRules({ top: { anchor: '__container__', align: VerticalAlign.Top }, left: { anchor: '__container__', align: HorizontalAlign.Start } }) Text('副标题') .id('sub') .alignRules({ top: { anchor: 'title', align: VerticalAlign.Bottom }, // 相对 title left: { anchor: 'title', align: HorizontalAlign.Start } }) } .width('100%') .height(200)__container__表示父容器本身。相对布局表达力强,但过度使用会难维护,简单场景优先用线性布局。
七、尺寸单位与自适应
ArkUI 支持多种尺寸写法:
| 写法 | 含义 |
|---|---|
100/100vp | 虚拟像素(自适应屏幕密度) |
'50%' | 父容器百分比 |
'1fr' | 弹性分配剩余空间 |
100.lpx | 物理像素(少用) |
vp(virtual pixel)是默认单位,系统按屏幕 DPI 自动换算,保证不同设备视觉一致。推荐用vp或%而非写死 px。
八、多端适配:一次开发,多端部署
鸿蒙强调"一多",同一套代码跑手机、平板、折叠屏。关键手段:
- 断点(Breakpoints):按窗口宽度分 sm/md/lg,切换布局。
import { Breakpoints } from '@kit.ArkUI'; @State currentBreakpoint: string = 'sm'; build() { GridRow({ breakpoints: { value: ['600vp', '840vp'], reference: BreakpointsReference.WindowSize } }) { GridCol({ span: { sm: 12, md: 6, lg: 4 } }) { ProductCard() } } }GridRow/GridCol配合断点,窗口变宽时自动从单列变多列,天然响应式。
- 媒体查询:
mediaquery监听屏幕方向/尺寸变化,动态切换。
import { mediaquery } from '@kit.ArkUI'; let listener = mediaquery.matchMediaSync('(orientation: landscape)'); listener.on('change', (m) => { this.isLandscape = m.matches; });九、滚动与嵌套滚动
内容超长用Scroll包裹:
Scroll() { Column() { ForEach(this.items, (it) => { Text(it).padding(12) }) } } .scrollable(ScrollDirection.Vertical) .scrollBar(BarState.Auto) .width('100%') .height('100%')Scroll与List/Grid都是滚动容器,但List/Grid自带虚拟滚动(大数据首选),Scroll适合少量内容的整体滚动。
十、常见布局坑
| 现象 | 原因 | 解决 |
|---|---|---|
| 子组件不显示 | 父容器没设宽高 | 给 Column/Row 设width('100%') |
| 文本被截断 | 没换行且空间不足 | 加.textOverflow+.maxLines |
| 百分比失效 | 父级无确定尺寸 | 确保父容器有具体宽高 |
| 多端错位 | 写死尺寸 | 用vp/%/fr+ 断点 |
十一、综合实战:商品详情页
@Entry @Component struct Detail { build() { Column() { // 顶部轮播图(Stack 叠指示器) Stack({ alignContent: Alignment.BottomCenter }) { Image($r('app.media.banner')).width('100%').height(220).objectFit(ImageFit.Cover) Row({ space: 4 }) { ForEach([0,1,2], () => { Stack().width(6).height(6).backgroundColor('#FFF').borderRadius(3) }) }.margin({ bottom: 8 }) } // 中部信息(Column) Column({ space: 8 }) { Text('鸿蒙开发板').fontSize(20).fontWeight(FontWeight.Bold) Text('¥299').fontColor('#FF4D4F').fontSize(18) Text('高性能,适合学习分布式能力').fontColor('#666') }.alignItems(HorizontalAlign.Start).padding(16) // 底部操作栏(Row 固定底) Row({ space: 12 }) { Button('加入购物车').layoutWeight(1) Button('立即购买').layoutWeight(1).backgroundColor('#FF4D4F') }.padding(12) } .width('100%') .height('100%') } }注意底部操作栏用layoutWeight(1)在 Row 内平分宽度——layoutWeight是 ArkUI 弹性布局的关键属性。
十二、总结
ArkUI 的布局体系覆盖了从线性到相对定位的全部需求:Column/Row/Flex处理一维排列,Stack处理层叠,Grid处理二维网格,RelativeContainer处理锚点关系。掌握justifyContent/alignItems/布局权重三件套,配合vp/%/fr单位和断点,即可实现既美观又多端自适应的界面。布局没有银弹——简单优先线性,复杂再用 Grid/Relative,保持可维护性。
