4、UI构建基础:Column/Row布局、Text/Image/Button组件、Flex布局与主轴对齐方式

说实话,很多初学者一上来就盯着复杂的动画和自定义绘制,结果连最基本的页面布局都写得歪歪扭扭。我个人习惯是,先把这几个基础组件和布局吃透,后面写复杂页面才能游刃有余。今天咱们就聊聊 ArkTS 里最常用的那几样东西。

4.1 Column 和 Row:最朴素的“上下”与“左右”

Column 和 Row 是啥?说白了,Column 就是让子组件从上往下排,Row 就是从左往右排。你想想看,一个 App 页面,90% 的结构都能用这两个东西搭出来。

核心区别:

  • Column:主轴是垂直方向(从上到下)
  • Row:主轴是水平方向(从左到右)

我在项目中遇到过一个问题:有个新手同事把一堆按钮用 Column 包起来,结果按钮之间挤得死死的。嗯,这里要注意,Column 和 Row 默认是不给子组件加间距的,你得手动设置 space 参数。

// 正确的间距写法
Column({ space: 12 }) {
  Text('第一行')
  Text('第二行')
  Text('第三行')
}
.width('100%')
.height(200)
.backgroundColor('#F5F5F5')

你看,加个 space: 12,子组件之间就有了 12vp 的间距。这个习惯我一直保持到现在,写布局第一件事就是先想好间距。

4.2 Text、Image、Button:三剑客

这三个组件,你写任何一个页面都绕不开。咱们一个一个说。

4.2.1 Text:不只是显示文字

Text 组件看起来简单,但坑不少。我曾经因为没设置 textAlign,导致文字在容器里偏左,怎么调都不对。后来才发现,Text 默认是左对齐的,你得显式指定。

Text('这是一段居中的文字')
  .fontSize(16)
  .fontColor('#333333')
  .textAlign(TextAlign.Center)  // 居中显示
  .width('100%')

还有一点,Text 支持富文本样式,比如加粗、斜体、下划线。但我不建议你在一个 Text 里塞太多样式,拆成多个 Text 组件,代码可读性会好很多。

4.2.2 Image:图片加载的坑

Image 组件,说白了就是显示图片。但有个细节很多人忽略:图片的缩放模式。默认情况下,Image 会保持原图尺寸,如果你的容器比图片小,图片就会溢出。

避坑指南:我曾经在项目里直接用了 Image 没设置 objectFit,结果图片把整个页面撑变形了。后来我养成了习惯,只要用 Image,必写 objectFit(ImageFit.Cover)ImageFit.Contain

Image($r('app.media.example'))
  .width(200)
  .height(150)
  .objectFit(ImageFit.Cover)  // 裁剪填充,保证铺满容器

另外,网络图片记得加占位图或加载失败的回退图,不然用户看到一片空白,体验很差。

4.2.3 Button:交互的入口

Button 组件,ArkTS 里提供了好几种样式:ButtonType.NormalButtonType.CapsuleButtonType.Circle。我个人习惯用 Capsule 风格,圆角看起来更现代。

Button('点击我')
  .type(ButtonType.Capsule)
  .backgroundColor('#007AFF')
  .fontColor('#FFFFFF')
  .fontSize(16)
  .width(200)
  .height(48)
  .onClick(() => {
    console.log('按钮被点击了')
  })

嗯,这里要注意,Button 的点击事件里不要写太重的逻辑,比如网络请求。我一般只在 onClick 里调用一个函数,把具体逻辑抽出去,这样代码好维护。

4.3 Flex 布局:更灵活的排列方式

Column 和 Row 虽然好用,但遇到复杂的自适应布局就有点力不从心了。这时候 Flex 布局就派上用场了。Flex 可以同时控制主轴和交叉轴的对齐方式,说白了就是更灵活。

Flex 的核心参数:

  • direction:主轴方向(FlexDirection.Row 或 FlexDirection.Column)
  • justifyContent:主轴对齐方式
  • alignItems:交叉轴对齐方式

举个例子,你想让三个按钮在水平方向上均匀分布,用 Row 加 space 很难做到完美,但 Flex 一行代码就搞定:

Flex({
  direction: FlexDirection.Row,
  justifyContent: FlexAlign.SpaceEvenly,
  alignItems: ItemAlign.Center
}) {
  Button('按钮1')
  Button('按钮2')
  Button('按钮3')
}
.width('100%')
.height(60)

你看,SpaceEvenly 会让所有按钮之间的间距相等,包括两端的间距。这个在写底部导航栏或者工具栏时特别常用。

4.4 主轴对齐方式:细节决定成败

主轴对齐方式,说白了就是子组件在主轴方向上的排列规则。Flex 提供了几种对齐方式,我列个表,你一看就明白:

对齐方式 说明 适用场景
FlexAlign.Start 从主轴起点开始排列 默认左对齐或上对齐
FlexAlign.Center 居中对齐 标题居中、弹窗内容
FlexAlign.End 从主轴终点开始排列 右下角悬浮按钮
FlexAlign.SpaceBetween 两端对齐,中间间距相等 导航栏、列表项
FlexAlign.SpaceAround 每个项目两侧间距相等 标签页、卡片布局
FlexAlign.SpaceEvenly 所有间距(含两端)相等 均匀分布的按钮组

为什么会这样?其实这些对齐方式是从 CSS 的 Flexbox 借鉴过来的,如果你写过前端,应该很熟悉。但 ArkTS 里有个小区别:主轴方向变了,对齐方式的效果也跟着变。比如 FlexDirection.Column 时,Start 就是顶部对齐,End 就是底部对齐。

小技巧:我写布局时,习惯先用 Center 对齐把内容居中,然后再微调。这样能快速看到整体效果,避免一开始就纠结边边角角。

4.5 实战:一个简单的登录卡片

说了这么多,咱们来写个实际例子。一个登录卡片,包含图标、输入框和按钮,用 Column、Row 和 Flex 组合实现:

Column({ space: 20 }) {
  // 顶部图标
  Image($r('app.media.logo'))
    .width(80)
    .height(80)
    .objectFit(ImageFit.Contain)

  // 输入框区域
  Column({ space: 12 }) {
    TextInput({ placeholder: '请输入用户名' })
      .width('100%')
      .height(44)
    TextInput({ placeholder: '请输入密码' })
      .width('100%')
      .height(44)
      .type(InputType.Password)
  }
  .width('100%')

  // 登录按钮
  Button('登录')
    .type(ButtonType.Capsule)
    .backgroundColor('#007AFF')
    .fontColor('#FFFFFF')
    .width('100%')
    .height(48)
    .onClick(() => {
      // 登录逻辑
    })

  // 底部链接
  Flex({
    direction: FlexDirection.Row,
    justifyContent: FlexAlign.SpaceBetween
  }) {
    Text('忘记密码')
      .fontColor('#666666')
      .fontSize(14)
    Text('注册账号')
      .fontColor('#007AFF')
      .fontSize(14)
  }
  .width('100%')
}
.width('90%')
.padding(20)
.backgroundColor('#FFFFFF')
.borderRadius(12)
.shadow({ radius: 8, color: '#1A000000' })

你看,这个登录卡片用 Column 做垂直布局,内部嵌套了 Row 和 Flex 做水平排列。整个结构清晰,而且每个组件都设置了合适的间距和对齐方式。我在实际项目中,这种卡片结构几乎每天都在用。

好了,这一章的内容就这些。Column/Row 是骨架,Text/Image/Button 是血肉,Flex 是关节。把这几个东西玩熟了,你写 UI 的效率至少提升一倍。下一章咱们聊聊更高级的布局技巧,比如相对布局和绝对定位,到时候见。