【共创季稿事节】鸿蒙原生ArkTS布局深度解析_GridRow_Row_Column混合栅格布局实战

鸿蒙原生 ArkTS 布局深度解析:GridRow + Row + Column 混合栅格布局实战


一、引言

在鸿蒙原生应用开发中,布局是构建用户界面的基石。HarmonyOS NEXT(API 24)提供了 ArkUI 声明式 UI 框架,其中包含了 GridRowGridColRowColumnFlexStack 等丰富的布局组件。开发者最常遇到的问题:实际项目中应选择哪种布局方式?如何组合以达到最佳效果?

本文从一个「数据仪表盘」Demo 出发,深入剖析 GridRow + Row + Column 混合栅格布局的核心原理和最佳实践。这套方案已通过多个鸿蒙商业项目验证,适用于管理后台、数据看板、内容资讯类 App 等场景。


二、布局组件基础知识

2.1 Row --- 弹性行容器

主轴方向 :水平(从左到右)。子元素通过 layoutWeight 分配剩余空间,通过 alignItems 控制交叉轴对齐。

typescript 复制代码
Row() {
  Text('左侧')
  Blank()   // 弹性占位,将右侧内容推到最右
  Text('右侧')
}
.width('100%')
.alignItems(VerticalAlign.Center)

2.2 Column --- 弹性列容器

Row 的垂直版本,子元素垂直排列,支持 alignItems(HorizontalAlign.Center) 控制水平对齐。适用于卡片、列表、表单等场景。

2.3 GridRow + GridCol --- 栅格容器

一对协同工作的布局组件,构成鸿蒙原生的 12 列栅格系统:

  • GridRow:定义列数、间距(gutter)和断点规则
  • GridCol :通过 span 指定占据的列数,支持响应式断点
typescript 复制代码
GridRow({
  columns: 12,
  gutter: { x: 16, y: 12 },
  breakpoints: { value: ['320dp', '600dp', '840dp'] }
}) {
  GridCol({ span: { xs: 12, sm: 6, md: 4, lg: 3 } }) {
    // 小屏占满宽,大屏占 1/4
  }
}

2.4 为什么需要混合布局?

单纯使用 GridRow 可解决「页面分区」问题,但无法灵活处理「区域内弹性排列」。单纯使用 Row/Column 可解决「弹性排列」,但缺乏结构化的页面划分。

混合布局 = GridRow 做骨架 + Row/Column 做血肉,既保证整体结构规整,又赋予每个区域内部灵活的弹性排版能力。


三、实战:数据仪表盘页面

3.1 页面整体结构

复制代码
Scroll
 └─ Column
     ├─ GridRow #1 ─── 标题栏(全宽 GridCol → Row + Column)
     ├─ GridRow #2 ─── 统计卡片(GridCol × 4 → @Builder StatCard)
     ├─ GridRow #3 ─── 混合内容区
     │   ├─ GridCol(left) → 内嵌 GridRow(3列) → 功能入口
     │   └─ GridCol(right) → Column → Row × N + Divider
     └─ GridRow #4 ─── 底部说明(Row + Column + Blank)

3.2 数据模型定义

typescript 复制代码
@Observed
class StatCardData {
  title: string; value: string; unit: string; color: Color;
  constructor(title: string, value: string, unit: string, color: Color) {
    this.title = title; this.value = value;
    this.unit = unit; this.color = color;
  }
}

@Observed
class FeatureItem {
  icon: ResourceStr; label: string; badge?: string;
  constructor(icon: ResourceStr, label: string, badge?: string) {
    this.icon = icon; this.label = label; this.badge = badge;
  }
}

3.3 响应式断点配置

断点 宽度 列数 gutter 布局形态
xs 0~319dp 4 8 极简布局
sm 320~599 4 8 小屏手机
md 600~839 8 12 平板竖屏
lg 840dp+ 12 16 平板横屏

通过 @Watch 监听断点变化:

typescript 复制代码
@State @Watch('onBreakpointChange') currentBreakpoint: string = 'sm';
@State currentCol: number = 4;
@State currentGutter: number = 12;

onBreakpointChange(): void {
  switch (this.currentBreakpoint) {
    case 'xs': case 'sm': this.currentCol = 4; this.currentGutter = 8; break;
    case 'md': this.currentCol = 8; this.currentGutter = 12; break;
    case 'lg': this.currentCol = 12; this.currentGutter = 16; break;
    default: this.currentCol = 12; this.currentGutter = 12;
  }
}

断点变化由 GridRow 内置事件驱动:

typescript 复制代码
GridRow({ columns: this.currentCol, gutter: this.currentGutter,
          breakpoints: { value: ['320dp', '600dp', '840dp'] } })
  .onBreakpointChange((breakpoint: string): void => {
    this.currentBreakpoint = breakpoint;
  })

3.4 标题栏(Row + Column 混合)

typescript 复制代码
GridCol({ span: { xs: 4, sm: 4, md: 8, lg: 12 } }) {
  Row() {
    Column() {
      Text('📊 数据仪表盘').fontSize(22).fontWeight(FontWeight.Bold);
      Text('GridRow + Row + Column 混合栅格布局演示')
        .fontSize(13).fontColor('#888888').margin({ top: 4 });
    }.alignItems(HorizontalAlign.Start);
    Blank();  // 弹性占位
    Text(this.currentBreakpoint.toUpperCase())
      .fontSize(12).fontColor(Color.White).backgroundColor('#3A86FF')
      .borderRadius(4).padding({ left: 10, right: 10, top: 4, bottom: 4 });
  }.width('100%').alignItems(VerticalAlign.Center);
}

关键技巧Blank() 充当弹性分隔器,将左右内容自动撑开。

3.5 统计卡片(响应式 span + @Builder)

typescript 复制代码
ForEach(this.statCards, (item: StatCardData) => {
  GridCol({ span: { xs: 2, sm: 2, md: 4, lg: 3 } }) {
    this.StatCard(item);
  }
})

响应式规则:xs/sm 总列数 4、span=2 → 一行 2 个;lg 总列数 12、span=3 → 一行 4 个。

卡片用 @Builder 抽离,内部 Column + Row 混合:

typescript 复制代码
@Builder
StatCard(item: StatCardData) {
  Column() {
    Text(item.title).fontSize(13).fontColor('#888888');
    Row() {
      Text(item.value).fontSize(26).fontWeight(FontWeight.Bold).fontColor(item.color);
      Text(item.unit).fontSize(13).fontColor('#888888')
        .margin({ left: 4 }).alignSelf(ItemAlign.End).padding({ bottom: 4 });
    }.alignItems(VerticalAlign.Center).margin({ top: 6 });
    Row() {
      Row().width('60%').height(4).borderRadius(2)
        .backgroundColor(item.color).opacity(0.6);
      Blank();
    }.width('100%').margin({ top: 10 });
  }.width('100%').padding(14).borderRadius(12).backgroundColor(Color.White)
   .shadow({ radius: 4, offsetX: 0, offsetY: 2, color: 'rgba(0,0,0,0.06)' });
}

注意@Builder 不包裹 GridCol,由父级负责布局职责,实现内容与布局的分离。

3.6 混合内容区(栅格套栅格 + 弹性列表)

GridRow #3 中包含两个 GridCol,各自内部采用不同的布局策略:

左侧 - 功能入口(嵌套 GridRow)

typescript 复制代码
GridCol({ span: { xs: 4, sm: 4, md: 8, lg: 8 } }) {
  Column() {
    Row() {
      Text('⚡ 快捷功能').fontSize(16).fontWeight(FontWeight.Medium);
      Blank();
      Text('全部 →').fontSize(12).fontColor('#3A86FF');
    }.width('100%').margin({ bottom: 12 });

    GridRow({ columns: 3, gutter: 10 }) {  // 内嵌栅格
      ForEach(this.features, (item: FeatureItem) => {
        GridCol({ span: 1 }) {
          Column() {
            Image(item.icon).width(40).height(40)
              .borderRadius(20).backgroundColor('#F0F4FF');
            Text(item.label).fontSize(13).fontColor('#333333').margin({ top: 6, bottom: 4 });
            if (item.badge !== undefined) {
              Text(item.badge).fontSize(10).fontColor(Color.White)
                .backgroundColor('#FF4757').borderRadius(8)
                .padding({ left: 6, right: 6, top: 2, bottom: 2 });
            }
          }.width('100%').alignItems(HorizontalAlign.Center).padding(8)
           .borderRadius(12).backgroundColor(Color.White)
           .shadow({ radius: 4, offsetX: 0, offsetY: 2, color: 'rgba(0,0,0,0.06)' });
        }
      })
    }.width('100%');
  }.width('100%').padding(12).borderRadius(12).backgroundColor('#F8F9FE');
}

嵌套链路:GridRow > GridCol > Column > GridRow > GridCol > Column,每层各司其职。

右侧 - 最近动态(Row + Divider)

typescript 复制代码
GridCol({ span: { xs: 4, sm: 4, md: 8, lg: 4 } }) {
  Column() {
    Row() {
      Text('📋 最近动态').fontSize(16).fontWeight(FontWeight.Medium);
      Blank();
      Text('更多 →').fontSize(12).fontColor('#3A86FF');
    }.width('100%').margin({ bottom: 12 });

    ForEach(this.activities, (activity: string, idx?: number) => {
      Column() {
        Row() {
          Column() { Circle().width(8).height(8).fill('#3A86FF'); }
            .width(20).alignItems(HorizontalAlign.Center);
          Text(activity).fontSize(13).fontColor('#555555');
        }.width('100%').alignItems(VerticalAlign.Center)
         .padding({ top: 8, bottom: 8 });
        if (idx! < this.activities.length - 1) {
          Divider().strokeWidth(1).color('#EEEEEE');
        }
      }
    })
  }.width('100%').padding(12).borderRadius(12).backgroundColor(Color.White)
   .shadow({ radius: 4, offsetX: 0, offsetY: 2, color: 'rgba(0,0,0,0.06)' });
}

注意Row 在 HarmonyOS NEXT 中不支持 borderBottom,改用 Column 包裹 + Divider 实现行间分隔。

3.7 底部说明

typescript 复制代码
GridCol({ span: { xs: 4, sm: 4, md: 8, lg: 12 } }) {
  Column() {
    Divider().strokeWidth(1).color('#E8E8E8').margin({ bottom: 12 });
    Row() {
      Column() {
        Text('📐 布局层级说明').fontSize(14).fontWeight(FontWeight.Medium);
        Text('GridRow > GridCol > Row/Column > 内容')
          .fontSize(12).fontColor('#999999').margin({ top: 4 });
      }.alignItems(HorizontalAlign.Start);
      Blank();
      Column() {
        Text('📱 响应式规则').fontSize(14).fontWeight(FontWeight.Medium);
        Text('xs/sm(4列)  md(8列)  lg(12列)')
          .fontSize(12).fontColor('#999999').margin({ top: 4 });
      }.alignItems(HorizontalAlign.End);
    }.width('100%').height(60).padding(12).borderRadius(8).backgroundColor('#F5F5F5');
  }.width('100%');
}

四、核心技术要点

4.1 GridRow 完整 API

typescript 复制代码
GridRow({
  columns?: number | GridRowColumnOption,  // 列数(默认 12)
  gutter?: Length | GutterOption,          // 间距
  breakpoints?: BreakpointsOption,         // 断点配置
  direction?: GridRowDirection,            // 排列方向
})

注意gutter 只接受 number 类型,不支持 number[]

4.2 GridCol 的响应式语法

typescript 复制代码
// 固定值
GridCol({ span: 4 })
// 响应式对象(推荐)
GridCol({ span: { xs: 12, sm: 6, md: 4, lg: 3 } })

4.3 布局组件选用原则

需求 推荐组件 原因
页面整体分区 GridRow+GridCol 结构清晰、自带响应式
水平排列元素 Row 弹性 + Blank() 分隔
垂直排列元素 Column 弹性、可嵌套 Scroll
卡片内部排版 Column+Row 灵活、轻量
固定列数网格 GridRow(固定col) 比 Flex 更规整
层叠 Stack 层叠上下文

4.4 常见陷阱

陷阱 说明 解决方案
Row 不支持 borderBottom Row 属性列表无该方法 外层 Column + Divider
gutter 类型错误 声明为 `number number[]` 会编译失败
断点监听方式错误 BreakpointObserver 未从 @kit.ArkUI 导出 用 GridRow 内置事件
@Builder 不应包含 GridCol @Builder 应返回纯内容组件,由父级负责布局 GridCol 在调用方处包裹
ForEach 缺少 key 自定义对象列表无 keyGenerator 会影响 Diff 性能 传入第三个 key 函数

五、进阶技巧

5.1 栅格套栅格

GridCol 内嵌套 GridRow 是最常见的复合模式。内层 GridRow 的列数可独立设置:

typescript 复制代码
GridCol({ span: 8 }) {
  GridRow({ columns: 3, gutter: 10 }) {  // 内层固定 3 列
    GridCol({ span: 1 }) { /* ... */ }
    GridCol({ span: 1 }) { /* ... */ }
  }
}

5.2 响应式排序

通过 order 属性控制不同断点下的排列顺序:

typescript 复制代码
GridCol({ span: { xs: 12, lg: 6 }, order: { xs: 2, lg: 1 } }) { /* 次要内容 */ }
GridCol({ span: { xs: 12, lg: 6 }, order: { xs: 1, lg: 2 } }) { /* 主要内容 */ }

5.3 Scroll 嵌套 GridRow

最外层 Scroll,直接子元素为 Column,内部放多个 GridRow:

typescript 复制代码
Scroll() {
  Column() {
    GridRow(/* ... */) { /* 标题栏 */ }
    GridRow(/* ... */) { /* 卡片区 */ }
    GridRow(/* ... */) { /* 内容区 */ }
  }.width('100%')
}.width('100%').height('100%')

5.4 @State 驱动栅格配置

复制代码
用户旋转设备 → GridRow.onBreakpointChange → @State 更新 → @Watch 触发 → UI 重绘

六、性能考量

  1. 减少嵌套层级:不超过 4 层(GridRow > GridCol > Row/Column > 内容)
  2. 大数据用 LazyForEach:超过 100 项时替代 ForEach
  3. @Builder 复用:高频渲染单元用 @Builder 提取,利于 ArkUI 优化

七、总结

GridRow + Row + Column 混合栅格布局是鸿蒙开发中最实用、最灵活的布局方案之一。

核心心法

  1. 外层用 GridRow 划分页面骨架
  2. 内层用 Row/Column 实现弹性排版
  3. 响应式 span 是适配利器
  4. Blank() 是最佳弹性助手
  5. @Builder 抽离内容组件

这套方案已在多个生产级鸿蒙 App 中验证,兼具可维护性和扩展性。

在这里插入图片描述


相关推荐
kiros_wang2 小时前
鸿蒙 ArkUI:V1 与 V2 装饰器全面对比与迁移指南
ubuntu·华为·harmonyos
古德new2 小时前
鸿蒙PC迁移:Photoflare Qt 图片编辑器鸿蒙PC适配全记录
qt·编辑器·harmonyos
不羁的木木2 小时前
HarmonyOS 6.1.0 创新特性技术精讲之沉浸光感
华为·harmonyos
JOJO数据科学3 小时前
JupyterLab Electron 鸿蒙 PC 适配全记录:从 Python 原生崩溃到 node-static 本地工作台
python·electron·harmonyos
CHB4 小时前
HDC2026 演讲实录|AI 驱动的跨端进化:利用 uni-agent 快速构建高性能鸿蒙应用
uni-app·harmonyos
祭曦念5 小时前
【共创季稿事节】鸿蒙ArkTS布局实战_Column交叉轴对齐
华为·harmonyos
古德new6 小时前
鸿蒙PC迁移:Anki Qt 记忆卡片工具鸿蒙PC适配全记录
qt·华为·harmonyos
TMT星球7 小时前
创梦天地《地铁跑酷》携手鸿蒙 深化全场景生态共建
华为·harmonyos
枫叶丹47 小时前
【HarmonyOS 6.0】MDM Kit 新特性:PC/2in1设备无锁屏密码重启自动解锁能力详解
开发语言·华为·harmonyos