鸿蒙原生 ArkTS 布局精讲:Column 与 Divider 搭配,分隔线的最佳实践


一、前言

在移动端应用开发中,「列表页」是最常见、最高频的界面形态之一。无论是系统设置、消息通知、联系人列表还是订单管理,几乎每个应用都离不开纵向列表。然而,列表页的设计难点不在于「把它们排出来」,而在于「如何让视觉层次清晰可辨」

很多开发者初学 ArkTS 时,习惯用 Column 一股脑堆砌所有条目,再手动加一些 marginpadding 来区分。这样做虽然能跑起来,但遇到复杂的分组列表时,视觉上往往一片混沌——用户分不清哪里是一个分组的结束、哪里是下一个分组的开始。

解决方案:合理使用 Divider(分隔线)组件,配合 Column 的纵向排列能力,构建出层次分明、呼吸感十足的列表界面。

本文将以一个完整的「设置页」Demo 为例,手把手带你掌握 Column + Divider 的最佳实践,涵盖分隔线的粗细控制、颜色搭配、缩进策略等实战技巧。


二、前置知识:Column 与 Divider 基础

在深入案例之前,先快速回顾一下这两个组件的核心特性。

2.1 Column:纵向排列容器

Column 是鸿蒙 ArkTS 中三大基础布局容器之一(另外两个是 RowFlex),子组件沿垂直方向从上到下依次排列

Column({ space: 12 }) {  // space 控制子项间距
  Text('第一项')
  Text('第二项')
  Text('第三项')
}
.width('100%')

关键属性:

属性 类型 说明
space number | string 子组件之间的间距,单位 vp
alignItems HorizontalAlign 水平对齐方式(Start / Center / End)
justifyContent FlexAlign 垂直方向主轴对齐方式

2.2 Divider:分隔线组件

Divider 是一个轻量的分割线组件,通常用于区分列表项、分组或内容区块。

Divider()
  .width('100%')       // 线宽
  .height(1)           // 线粗(垂直方向厚度)
  .color('#E8E8E8')    // 颜色
  .strokeLineCap(LineCapStyle.Round)  // 线帽样式
  .margin({ left: 16, right: 16 })   // 外边距

关键属性:

属性 类型 默认值 说明
width Length ‘100%’ 分隔线的宽度
height Length 1 分隔线的粗细(厚度)
color ResourceColor ‘#33182431’ 线条颜色
strokeLineCap LineCapStyle Butt 线帽样式:Butt(平头)、Round(圆头)、Square(方头)

注意:在 ArkTS 中,Divider 默认是一个水平线,height 控制线的粗细。


三、案例实战:仿系统「设置页」

光说不练假把式。我们用一个真实的 Demo——仿 HarmonyOS 系统设置页——来演示 Column + Divider 的组合拳。

3.1 最终效果预览

页面结构如下:

┌─────────────────────────────────┐
│  ⚙️ 设置              v1.0.0    │  ← 顶部标题栏
├─────────────────────────────────┤
│  个人信息                        │  ← 分组标题(小字灰色)
│  👤 账号与安全                    │
│     密码、指纹、面部识别        │
│  ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─  │  ← 细分隔线(0.5px,缩进52vp)
│  📱 隐私                         │
│     权限管理、广告追踪          │
│  ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─  │
│  🔒 密码与账户                   │
│     应用密码、自动填充          │
├═════════════════════════════════┤  ← 粗分隔线(8px,与背景同色=留白)
│  通知与显示                      │
│  🔔 通知管理                     │
│  ...                            │
├═════════════════════════════════┤
│  其他设置                        │
│  ...                            │
└─────────────────────────────────┘

3.2 完整代码

/**
 * Column × Divider 最佳实践
 * ──────────────────────────────
 * 场景:用 Divider 提升纵向列表的视觉层次
 * 核心技术:Divider, Column, 分隔线样式
 *
 * 布局要点:
 * 1. Column 作为纵向排列容器,Divider 作为分隔元素嵌入其中
 * 2. Divider 支持多种样式:粗细(height)、颜色(color)、线帽样式(strokeLineCap)
 * 3. 合理运用 Divider 的 margin 可增强视觉呼吸感
 * 4. 不同粗细/颜色的 Divider 可表达不同的层次关系
 * 5. 使用 @Builder 将重复 UI 片段抽离为构建函数
 */
@Entry
@Component
struct DividerColumnDemo {
  // ─── 模拟的列表数据 ───
  @State private sections: SectionItem[] = [
    {
      title: '个人信息',
      items: [
        { icon: '\u{1F464}', label: '账号与安全', desc: '密码、指纹、面部识别' },
        { icon: '\u{1F4F1}', label: '隐私', desc: '权限管理、广告追踪' },
        { icon: '\u{1F512}', label: '密码与账户', desc: '应用密码、自动填充' }
      ]
    },
    {
      title: '通知与显示',
      items: [
        { icon: '\u{1F514}', label: '通知管理', desc: '应用通知、锁屏通知' },
        { icon: '\u{1F4FA}', label: '显示与亮度', desc: '深色模式、字体大小' },
        { icon: '\u{1F30D}', label: '壁纸与个性化', desc: '壁纸、主题、图标' }
      ]
    },
    {
      title: '其他设置',
      items: [
        { icon: '\u{1F4E6}', label: '应用管理', desc: '应用列表、默认应用' },
        { icon: '\u{26A1}', label: '电池', desc: '省电模式、耗电排行' },
        { icon: '\u{1F527}', label: '系统更新', desc: '检查更新、自动更新' }
      ]
    }
  ];

  // ─── 构建页面入口 ───
  build() {
    Column() {
      // 顶部标题栏(直接使用 @Builder)
      this.buildTitleBar();

      // 可滚动的列表主体
      Scroll() {
        Column() {
          // 遍历每个分组
          ForEach(this.sections, (section: SectionItem, index: number) => {
            // 分组间插入粗分隔线(第一个分组前不显示)
            if (index > 0) {
              this.buildSectionDivider();
            }
            // 分组标题
            this.buildSectionTitle(section.title);
            // 分组内列表项
            ForEach(section.items, (item: ListItemData, itemIndex: number) => {
              this.buildListItem(item, itemIndex, section.items.length);
            })
          })
        }
        .width('100%')
        .padding({ left: 16, right: 16, top: 12, bottom: 24 })
      }
      .scrollBar(BarState.Off)           // 隐藏滚动条,保持界面清爽
      .edgeEffect(EdgeEffect.Spring)     // 边缘回弹效果
      .width('100%')
      .layoutWeight(1)                   // 填充剩余空间
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#F2F3F5')          // 浅灰背景,衬托卡片区域
  }

  // ════════════════════════════════════
  //  @Builder:可复用的 UI 片段
  // ════════════════════════════════════

  @Builder
  buildTitleBar() {
    Column() {
      Row() {
        Text('\u{2699}\u{FE0F}')
          .fontSize(22)
        Text('设置')
          .fontSize(20)
          .fontWeight(FontWeight.Bold)
          .margin({ left: 8 })
        Blank()
        Text('v1.0.0')
          .fontSize(13)
          .fontColor('#999')
      }
      .width('100%')
      .padding({ left: 16, right: 16, top: 12, bottom: 12 })
    }
    .width('100%')
    .backgroundColor('#FFFFFF')
  }

  @Builder
  buildSectionDivider() {
    Column() {
      Divider()
        .width('100%')
        .height(8)
        .color('#F2F3F5')
        .margin({ top: 8, bottom: 4 })
    }
    .width('100%')
  }

  @Builder
  buildSectionTitle(title: string) {
    Text(title)
      .fontSize(13)
      .fontColor('#666666')
      .fontWeight(FontWeight.Medium)
      .margin({ top: 4, bottom: 8, left: 4 })
      .width('100%')
  }

  @Builder
  buildListItem(item: ListItemData, itemIndex: number, totalCount: number) {
    Column() {
      Row() {
        Text(item.icon)
          .fontSize(22)
          .width(36)
          .height(36)
          .textAlign(TextAlign.Center)
          .lineHeight(36)
        Column() {
          Text(item.label)
            .fontSize(16)
            .fontColor('#1A1A1A')
            .fontWeight(FontWeight.Medium)
          Text(item.desc)
            .fontSize(12)
            .fontColor('#999999')
        }
        .margin({ left: 12 })
        .layoutWeight(1)
        Text('>')
          .fontSize(16)
          .fontColor('#CCCCCC')
      }
      .width('100%')
      .padding({ top: 14, bottom: 14, left: 12, right: 12 })
      .alignItems(VerticalAlign.Center)

      if (itemIndex < totalCount - 1) {
        Divider()
          .width('100%')
          .height(0.5)
          .color('#E8E8E8')
          .margin({ left: 52 })
      }
    }
    .width('100%')
    .backgroundColor('#FFFFFF')
  }
}

interface ListItemData {
  icon: string;
  label: string;
  desc: string;
}

interface SectionItem {
  title: string;
  items: ListItemData[];
}

四、逐层解析:Column + Divider 的设计密码

4.1 第一层:全局 Column + Scroll

Column() {
  this.buildTitleBar();
  Scroll() {
    Column() { /* 列表内容 */ }
  }
  .layoutWeight(1)
}

整个页面是一个大的 Column,包含标题栏和可滚动列表。关键点:

  • layoutWeight(1):让 Scroll 自动填充标题栏下方的所有剩余空间,无需手动计算高度。
  • Scroll + Column:内层 Column 负责纵向排列所有分组和列表项,外层 Scroll 提供内容溢出时的滚动能力。
  • edgeEffect(EdgeEffect.Spring):滚动到边缘时有弹性回弹效果,提升交互质感。

4.2 第二层:双轨 Divider 体系

这是本文最核心的设计模式——建立「粗细两档」分隔线体系

粗分隔线(分组级):height(8), color('#F2F3F5')
Divider()
  .width('100%')
  .height(8)
  .color('#F2F3F5')

作用:表示「章节切换」。颜色与页面背景色 #F2F3F5 相同,所以视觉上看起来不是一个「线」,而是一段留白间隔——这比直接用 margin 更优雅,因为 Divider 占据了布局中的独立位置,不会受相邻元素 margin 折叠的影响。

细分隔线(列表项级):height(0.5), color('#E8E8E8'), margin({left: 52})
Divider()
  .width('100%')
  .height(0.5)
  .color('#E8E8E8')
  .margin({ left: 52 })

作用:表示「同组内相邻项」。这里的精妙之处在于:

  1. 0.5px 极细:视觉上若有若无,不会抢夺内容的注意力。
  2. 缩进 52vp:从图标右侧开始对齐,避免分隔线「贯穿」左侧图标区域,视觉上更干净。
  3. 最后一项不画:通过 if (itemIndex < totalCount - 1) 条件控制,末尾无分隔线,让卡片底部留有完整白边。
双轨对比
维度 粗分隔线 细分隔线
height 8vp 0.5vp
color #F2F3F5(背景同色) #E8E8E8(浅灰)
作用 分组之间的大间隔 列表项之间的微分割
视觉效果 留白区块 若有若无的细线
缩进 无(全宽) left: 52vp

4.3 第三层:@Builder 抽离 UI 结构

在 ArkTS 中,build() 方法内只能使用声明式 UI 语法。如果像传统面向对象编程那样定义普通成员方法并在 build() 中调用,编译器会报错:

'this.buildTitleBar();' does not meet UI component syntax.

正确的做法是使用 @Builder 装饰器:

@Builder
buildListItem(item: ListItemData, itemIndex: number, totalCount: number) {
  // 这里可以写 Column, Row, Text, Divider 等任何 UI 组件
}

@Builder 的优点:

  • 复用性:相同结构的 UI 片段只需定义一次
  • 参数化:可以接收外部传入的数据和状态
  • 可嵌套@Builder 内部可以调用另一个 @Builder
  • 类型安全:参数有明确的类型定义,IDE 可自动补全

4.4 第四层:数据驱动渲染

我们用了两组 ForEach

// 外层:遍历分组
ForEach(this.sections, (section: SectionItem, index: number) => { ... })

// 内层:遍历某分组内的所有列表项
ForEach(section.items, (item: ListItemData, itemIndex: number) => { ... })

这种「双层 ForEach」模式非常适用于分组列表场景。如果将来需要增删改列表项,只需修改 @State 装饰的 sections 数组,UI 会自动响应更新。


五、进阶技巧与避坑指南

5.1 分隔线颜色使用语义化色板

不要随手写一个颜色值。推荐为分隔线建立语义化色板:

token 用途
--divider-strong #E8E8E8 列表项分隔线
--divider-section #F2F3F5 分组间隔(与背景同色)
--divider-card-border #DCDCDC 卡片描边

5.2 Divider 的 strokeLineCap 妙用

当分隔线较短(非全宽)时,strokeLineCap 可以改变线条端点的形状:

Divider()
  .width(80)                    // 固定宽度而非 100%
  .height(2)
  .strokeLineCap(LineCapStyle.Round)   // 圆头端点
  .color('#007AFF')

效果:一条两端圆润的短蓝线,常用于「查看更多」之类的装饰性分割。

5.3 避免 margin 折叠陷阱

在 CSS 中,相邻元素的 margin 会折叠(取最大值)。但在 ArkTS 中,Column 的子组件之间的 margin 不会折叠。不过用 Divider 代替 margin 来制造间隔有一个额外好处——Divider 是独立组件,可以单独设置 hitTestBehavior,不会意外拦截点击事件。

5.4 与 List 组件的权衡

ArkTS 提供了专门的 List + ListItem 组件用于长列表。那为什么不直接用 List

场景 推荐方案 理由
简单的同构列表 List + ListItem 懒加载、回收机制,性能更好
复杂的分组布局 Scroll + Column + Divider 布局更灵活,可嵌入任意嵌套结构
卡片式列表 Scroll + Column 每个卡片可以独立设置背景、圆角、阴影

本案例采用 Scroll + Column 就是因为分组标题 + 列表项 + 分隔线的异构结构在 Column 中表达更自然。

5.5 Divider 与空白占位的搭配

除了分隔线本身,Divider 还可以配合空白组件实现更丰富的视觉效果:

Row() {
  Divider().height(1).color('#E0E0E0').layoutWeight(1)
  Text('  OR  ').fontSize(12).fontColor('#999')
  Divider().height(1).color('#E0E0E0').layoutWeight(1)
}

这是登录页常见的「OR 分割线」实现方式。


六、性能要点

对于本案例(3 组 × 3 项 = 9 个列表项),Scroll + Column 模式的性能完全足够。但如果列表规模增长到几百项,建议关注以下优化点:

  1. ForEach 的 keyGenerator:给每个列表项提供唯一的 key,帮助框架精准复用组件。
  2. 避免 @Builder 内的冗余重建:如果 @Builder 的参数是复杂对象,确保数据不可变以触发最小更新。
  3. LazyForEach 替代 ForEach:数据量超过 50 项时,改用 LazyForEach 实现懒加载。
// 大数据量时使用 LazyForEach
LazyForEach(this.dataSource, (item: DataItem, index: number) => {
  ListItem() { /* ... */ }
}, (item: DataItem) => item.key)

七、扩展:给列表加上「卡片化」样式

如果觉得现在的全宽列表略显单调,可以轻松升级为卡片式设计——只需要在外层 Column 添加背景色和圆角:

@Builder
buildCardGroup(section: SectionItem) {
  Column() {
    this.buildSectionTitle(section.title);
    ForEach(section.items, (item: ListItemData, itemIndex: number) => {
      this.buildListItem(item, itemIndex, section.items.length);
    })
  }
  .width('100%')
  .backgroundColor('#FFFFFF')
  .borderRadius(12)           // 圆角
  .shadow({                   // 阴影
    radius: 4,
    color: 'rgba(0,0,0,0.06)',
    offsetX: 0,
    offsetY: 2
  })
  .padding(0)
}

然后分组之间的粗分隔线保留不变,这样就变成了一张张「卡片」堆叠的效果——视觉上更接近 iOS 的设置页风格。


八、写在最后

通过本文的完整案例,我们深入探索了鸿蒙 ArkTS 中 ColumnDivider 的搭配技巧。总结核心要点:

  1. 粗细分级:建立粗(分组级)与细(项级)两档分隔线,让视觉层次一目了然。
  2. 颜色即留白:分隔线颜色与背景色相同,就能制造出「无形的间距块」。
  3. 缩进对齐:列表项分隔线从图标右侧开始,避免贯穿图标区域。
  4. @Builder 抽离:将重复的 UI 片段封装为 @Builder 方法,保持代码整洁。
  5. Scroll + Column 组合:灵活应对异构分组列表,比 List 组件更可控。

分隔线虽小,却是界面设计中不可或缺的「视觉标点符号」。用好 ColumnDivider 的组合,你的鸿蒙应用列表页面将从此告别「一团和气」的平庸感,呈现专业级的视觉层次。

最后,建议你打开 DevEco Studio,创建一个新的 ArkTS 项目,把本文的 Demo 代码贴进去运行看看——眼见为实,亲手调试一次比读十遍文章更有收获。


本文配套代码已通过 HarmonyOS NEXT API 24 编译验证,可在 DevEco Studio 5.0+ 中直接运行。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐