4、自适应间距与对齐:掌握justifyContent和alignItems在不同主轴方向上的应用,实现动态间距

好,咱们接着聊布局。前面几章我们把 Flex 容器的基础概念过了一遍,今天要聊的这两个属性——justifyContentalignItems——可以说是 Flex 布局的灵魂所在。

说白了,一个控制主轴方向上的排列,一个控制交叉轴方向上的对齐。你想想看,我们做多设备适配,最头疼的是什么?不就是不同屏幕尺寸下,元素之间的间距和对齐方式怎么自动调整吗?这两个属性就是干这个的。

4.1 主轴方向:justifyContent 的几种玩法

先说说 justifyContent。它控制的是主轴方向上的间距分配。我个人的习惯是,先把主轴方向定下来,再考虑用哪种间距策略。

鸿蒙里支持这么几种值:

属性值 效果说明 适用场景
FlexAlign.Start 从主轴起点开始排列 默认行为,左对齐或上对齐
FlexAlign.Center 居中对齐 弹窗、卡片内容居中
FlexAlign.End 从主轴终点开始排列 右下角操作按钮
FlexAlign.SpaceBetween 两端对齐,元素间间距相等 导航栏、工具栏
FlexAlign.SpaceAround 每个元素两侧间距相等 图标排列、标签组
FlexAlign.SpaceEvenly 所有间距(含两端)完全相等 均匀分布的菜单项

我在项目中遇到过一个问题:用 SpaceBetween 做底部导航栏,结果在平板上一看,两端的图标贴边太紧,视觉上很不舒服。后来换成 SpaceEvenly,两端的间距和中间一样,整体就协调多了。

核心要点:SpaceBetween 和 SpaceEvenly 的区别在于两端是否有间距。如果你希望元素不贴边,用 SpaceEvenly;如果希望充分利用空间,用 SpaceBetween。

4.2 交叉轴方向:alignItems 的妙用

alignItems 控制的是交叉轴方向上的对齐。嗯,这里要注意:交叉轴的方向取决于主轴。如果主轴是横向(Row),交叉轴就是纵向(Column),反之亦然。

鸿蒙里支持的取值:

属性值 效果说明 个人经验
ItemAlign.Start 交叉轴起点对齐 默认行为,顶部对齐
ItemAlign.Center 交叉轴居中对齐 最常用,垂直居中
ItemAlign.End 交叉轴终点对齐 底部对齐场景
ItemAlign.Stretch 拉伸填满交叉轴 等高布局,非常实用
ItemAlign.Baseline 文本基线对齐 文字大小不一时保持整齐

我曾经踩过一个坑:做列表项时,左边是图标,右边是两行文字。我用了 ItemAlign.Center,结果图标和文字的中线对上了,但文字行高不同,看起来歪歪扭扭的。后来换成 ItemAlign.Baseline,所有文本的基线对齐,视觉效果立刻好了很多。

小技巧:当容器高度不确定时,用 Stretch 可以让所有子元素等高,省去手动设置高度的麻烦。我在做卡片列表时经常这么用。

4.3 组合实战:动态间距的两种模式

光说理论没意思,咱们直接看代码。我总结了两种最常见的动态间距模式。

模式一:自适应等间距排列

这种模式适合导航栏、标签页。不管屏幕多宽,元素之间的间距自动均匀分配。

@Entry
@Component
struct AdaptiveSpacingDemo {
  build() {
    Column() {
      // 横向等间距导航栏
      Flex({
        direction: FlexDirection.Row,
        justifyContent: FlexAlign.SpaceEvenly,
        alignItems: ItemAlign.Center
      }) {
        Text('首页').fontSize(16)
        Text('分类').fontSize(16)
        Text('发现').fontSize(16)
        Text('我的').fontSize(16)
      }
      .width('100%')
      .height(50)
      .backgroundColor('#F5F5F5')

      // 纵向等间距菜单
      Flex({
        direction: FlexDirection.Column,
        justifyContent: FlexAlign.SpaceBetween,
        alignItems: ItemAlign.Start
      }) {
        Text('设置').fontSize(16)
        Text('账号').fontSize(16)
        Text('关于').fontSize(16)
        Text('退出').fontSize(16)
      }
      .width(200)
      .height(200)
      .backgroundColor('#FFFFFF')
      .margin({ top: 20 })
    }
    .width('100%')
    .padding(16)
  }
}

模式二:两端对齐 + 垂直居中

这种模式在表单、列表项中非常常见。左边是标签,右边是操作按钮或输入框。

@Entry
@Component
struct AlignDemo {
  build() {
    Column() {
      // 列表项:两端对齐,垂直居中
      Flex({
        direction: FlexDirection.Row,
        justifyContent: FlexAlign.SpaceBetween,
        alignItems: ItemAlign.Center
      }) {
        Text('用户名').fontSize(16)
        Text('点击修改 >').fontSize(14).fontColor('#666')
      }
      .width('100%')
      .height(48)
      .padding({ left: 16, right: 16 })
      .backgroundColor('#FFFFFF')
      .border({ width: { bottom: 1 }, color: '#E0E0E0' })

      // 卡片:内容居中
      Flex({
        direction: FlexDirection.Column,
        justifyContent: FlexAlign.Center,
        alignItems: ItemAlign.Center
      }) {
        Text('📦').fontSize(40)
        Text('暂无数据').fontSize(14).fontColor('#999')
      }
      .width('100%')
      .height(200)
      .backgroundColor('#FAFAFA')
      .borderRadius(8)
      .margin({ top: 16 })
    }
    .width('100%')
    .padding(16)
  }
}

注意:主轴和交叉轴的方向不要搞混了。我刚开始学的时候,经常把 justifyContent 和 alignItems 用反。记住一个口诀:justify 管主轴方向,align 管交叉轴方向。

4.4 避坑指南:我踩过的三个坑

做自适应布局,有些细节不注意,效果就会差很多。分享几个我实际项目中遇到的坑。

  1. 子元素宽度固定导致 SpaceBetween 失效
    如果你给子元素设置了固定宽度,而且总宽度超过了容器宽度,SpaceBetween 就不会均匀分布了。解决办法:用百分比宽度或者不设宽度,让 Flex 自己计算。
  2. alignItems: Stretch 不生效
    这个我遇到过好几次。Stretch 生效的前提是子元素没有设置固定高度(或宽度,取决于交叉轴方向)。如果你给子元素设了固定尺寸,Stretch 就无效了。
  3. 多行文本的基线对齐问题
    不同字体大小、不同行高的文本,用 Center 对齐会显得不整齐。这时候用 Baseline 效果更好。但要注意,Baseline 只对文本元素有效,对图片或按钮无效。

4.5 多设备适配的最佳实践

最后,说说我在多设备适配中的一些心得。

手机屏幕窄,适合用 SpaceBetweenSpaceEvenly,让元素充分利用横向空间。平板屏幕宽,用 CenterStart 更合适,避免元素间距过大显得松散。

我一般会这样搭配:

  • 手机端:justifyContent 用 SpaceBetween,alignItems 用 Center
  • 平板端:justifyContent 用 Start 或 Center,alignItems 用 Start
  • 折叠屏:根据展开状态动态切换 justifyContent 的值

说白了,没有一套配置能通吃所有设备。你需要根据屏幕宽度,动态调整这些属性。鸿蒙的媒体查询可以帮我们做这件事,这个后面章节会详细讲。

好,这一章的内容就到这里。记住:justifyContent 管间距,alignItems 管对齐。把这两个玩明白了,大部分布局问题都能迎刃而解。