ArkUI 布局系统:线性、弹性、层叠、栅格怎么选

布局决定组件在哪里

组件"长什么样"是组件属性的事,组件"放在哪"是布局的事。ArkUI 提供了多种布局容器,每种解决一类排列问题。选对布局,界面适配就顺;选错布局,写一堆 margin 硬调位置,换个屏幕就乱。

线性布局 Column / Row

最基础的布局:Column 纵向排列,Row 横向排列。

@Entry
@Component
struct LinearDemo {
  build() {
    Column({ space: 10 }) {  // space:子组件间距
      Text('第一行')
      Text('第二行')
      Text('第三行')
    }
    .padding(16)
  }
}

线性布局的核心属性是对齐方式:

Column({ space: 10 }) {
  Text('子组件 A')
  Text('子组件 B')
}
.justifyContent(FlexAlign.Center)  // 主轴(纵向)对齐:居中
.alignItems(HorizontalAlign.Center)  // 交叉轴(横向)对齐:居中

justifyContent 控制主轴(Column 是纵向,Row 是横向),alignItems 控制交叉轴。两者配合能实现大部分居中、分散需求。

弹性布局 Flex

Flex 是线性布局的增强版,子组件可以弹性伸缩。当需要"占满剩余空间"或"按比例分配"时用它:

@Entry
@Component
struct FlexDemo {
  build() {
    Flex({ direction: FlexDirection.Row, wrap: FlexWrap.NoWrap }) {
      Text('固定宽度')
        .width(100)
      Text('弹性占满剩余')
        .flexGrow(1)  // 占满剩余空间
      Text('固定宽度')
        .width(100)
    }
    .padding(16)
  }
}

flexGrow(1) 让中间组件占满剩余空间。这在导航栏(左右按钮中间标题)场景非常常用:

Row() {
  Image($r('app.media.back')).width(24).height(24)  // 左侧返回
  Text('页面标题')
    .fontSize(18)
    .layoutWeight(1)  // 占满中间,实现标题居中
    .textAlign(TextAlign.Center)
  Image($r('app.media.more')).width(24).height(24)  // 右侧更多
}
.width('100%')
.height(56)
.padding({ left: 16, right: 16 })

layoutWeight 是 Row/Column 里分配剩余空间的属性,配合 flexGrow 效果类似。

层叠布局 Stack

Stack 让子组件叠在一起,后声明的在上面。角标、悬浮按钮、图片上的文字都用它:

@Entry
@Component
struct StackDemo {
  build() {
    Stack({ alignContent: Alignment.TopEnd }) {  // 对齐方式:右上角
      // 底层:图片
      Image($r('app.media.avatar'))
        .width(80).height(80)
        .borderRadius(40)

      // 顶层:角标
      Text('9')
        .width(20).height(20)
        .fontSize(12)
        .fontColor(Color.White)
        .backgroundColor(Color.Red)
        .borderRadius(10)
        .textAlign(TextAlign.Center)
    }
  }
}

alignContent 指定所有子组件的对齐位置,Alignment.TopEnd 是右上角。角标在头像上的遮罩场景用 Stack 最合适。

相对布局 RelativeContainer

相对布局让子组件相对其他组件或容器定位。在需要"组件 B 在组件 A 下方 10vp"这种相对关系时使用:

@Entry
@Component
struct RelativeDemo {
  build() {
    RelativeContainer() {
      Text('标题')
        .id('title')  // 给组件设置 id
        .fontSize(20)

      Button('操作按钮')
        .id('btn')
        .alignRules({
          bottom: { anchor: 'title', align: VerticalAlign.Bottom },
          left: { anchor: '__container__', align: HorizontalAlign.Start }
        })
    }
    .height(200)
    .width('100%')
  }
}

alignRules 定义对齐规则,anchor 指向参照组件的 id(__container__ 是容器自身)。相对布局语法相对繁琐,能用线性/弹性布局解决就别用它。

栅格布局 GridRow / GridCol

栅格布局把界面分成 12 列,组件占几列灵活分配。多设备适配的响应式场景用它:

@Entry
@Component
struct GridRowDemo {
  build() {
    GridRow({ columns: 12, gutter: 10 }) {  // 12 列,列间距 10
      GridCol({ span: 6 }) {  // 占 6 列(一半)
        Text('左侧').height(100).backgroundColor('#FFE0E0').width('100%')
      }
      GridCol({ span: 6 }) {  // 占 6 列(一半)
        Text('右侧').height(100).backgroundColor('#E0E0FF').width('100%')
      }
    }
    .padding(16)
  }
}

栅格布局的优势是响应式——可以针对不同屏幕宽度设置不同的列分配合xssmmdlg` 断点),实现一套代码适配手机、平板。

布局选型指南

需求                                    → 布局
────────────────────────────────────────
子组件垂直排列                          → Column
子组件水平排列                          → Row
需要弹性伸缩/占满剩余                    → Flex / layoutWeight
组件叠加(角标、悬浮)                   → Stack
组件相对定位                            → RelativeContainer
响应式多列分配                          → GridRow / GridCol
长列表/宫格                             → List / Grid(见上一篇)

嵌套布局:用例子理解

真实界面很少有单一布局,都是嵌套的。一个常见的"设置项"界面:

@Entry
@Component
struct SettingsDemo {
  build() {
    Column() {
      // 设置项:左侧文字 + 右侧箭头
      Row() {
        Text('账号与安全')
          .fontSize(16)
          .layoutWeight(1)  // 占满左侧
        Image($r('app.media.arrow_right'))
          .width(20).height(20)
      }
      .height(56)
      .padding({ left: 16, right: 16 })
    }
    .width('100%')
  }
}

每个设置项是横向 Row,左边文字用 layoutWeight 占满,右边放箭头。外层是纵向 Column 堆叠多个设置项。这个模式几乎每个 App 都有。

布局的几条经验

  1. 优先考虑弹性伸缩。硬编码 width(300) 换屏就出问题,用 layoutWeight / flexGrow 让组件自适应,适配性更好。

  2. 居中需求分场景。单轴居中用 justifyContent / alignItems,组件本身居中且是唯一子组件时可以用 Stack + alignContent

  3. spacegutter 控制间距。别每个子组件都写 margin,容器级的 space(线性/List)或 GridRow 的 gutter 统一控制间距更干净。

  4. 嵌套别太深。布局容器套三层以上就应该考虑拆自定义组件,深嵌套既难维护也影响布局性能。

  5. 栅格布局别滥用。简单的左右分栏用 Row + layoutWeight 就够,只有真正需要响应式多列分配时才上 GridRow

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐