ArkUI RelativeContainer:复杂对齐别全靠嵌套

举报
蓝瘦的蜕变 发表于 2026/07/31 11:15:03 2026/07/31
【摘要】 今天我们用 RelativeContainer 做几种相对定位:对齐容器、对齐兄弟组件,再补一个辅助线和屏障的小例子。 这篇不讲完整约束布局系统。我们先用一个小页面把锚点关系看清楚。 关键组件要有 id 在 RelativeContainer 里,组件之间要互相参考位置,就得先有稳定 id: container 表示容

ArkUI RelativeContainer:复杂对齐别全靠嵌套

今天我们用 RelativeContainer 做几种相对定位:对齐容器、对齐兄弟组件,再补一个辅助线和屏障的小例子。

这篇不讲完整约束布局系统。我们先用一个小页面把锚点关系看清楚。

关键组件要有 id

RelativeContainer 里,组件之间要互相参考位置,就得先有稳定 id

Text('左侧')
  .id('leftBadge')
  .alignRules({
    left: { anchor: '__container__', align: HorizontalAlign.Start },
    center: { anchor: '__container__', align: VerticalAlign.Center }
  })

__container__ 表示容器本身。这里的意思是:左边对齐容器左边,垂直中心对齐容器中心。

兄弟组件也能当锚点

第二个组件可以贴着第一个组件走:

Text('B 贴在 A 右侧')
  .id('cardB')
  .alignRules({
    left: { anchor: 'cardA', align: HorizontalAlign.End },
    top: { anchor: 'cardA', align: VerticalAlign.Top }
  })

这类写法适合信息卡、头像加说明、局部操作区。它能减少一些无意义的 Row/Column 嵌套。

辅助线和屏障

guideLine 可以提供虚拟锚点,barrier 可以根据一组组件的边界生成参考线。先别把它想复杂,记住它们都是“给后面的对齐多一个参照物”。

你现在项目里有没有那种嵌套了很多层,只是为了让两个组件对齐的 UI?可以拿一小块出来试着改成 RelativeContainer

完整代码

@Entry
@Component
struct ArkUIRelativeContainerDemo {
  @Builder
  sectionTitle(title: string, desc: string) {
    Column({ space: 4 }) {
      Text(title)
        .width('100%')
        .fontSize(16)
        .fontWeight(700)
        .fontColor('#0F172A')

      Text(desc)
        .width('100%')
        .fontSize(13)
        .fontColor('#64748B')
        .lineHeight(20)
    }
    .width('100%')
    .alignItems(HorizontalAlign.Start)
  }

  @Builder
  badge(text: string, color: string) {
    Text(text)
      .height(36)
      .padding({ left: 12, right: 12 })
      .fontSize(13)
      .fontWeight(700)
      .fontColor('#FFFFFF')
      .textAlign(TextAlign.Center)
      .backgroundColor(color)
      .borderRadius(8)
  }

  build() {
    Scroll() {
      Column({ space: 18 }) {
        Text('ArkUI RelativeContainer')
          .width('100%')
          .fontSize(24)
          .fontWeight(700)
          .fontColor('#0F172A')

        Text('复杂对齐别全靠嵌套')
          .width('100%')
          .fontSize(22)
          .fontWeight(700)
          .fontColor('#111827')

        Text('RelativeContainer 适合处理多个元素互相参考位置的页面片段。这里只看锚点、辅助线和简单屏障,不展开完整约束系统。')
          .width('100%')
          .fontSize(14)
          .fontColor('#64748B')
          .lineHeight(22)

        Column({ space: 12 }) {
          this.sectionTitle('先给关键组件写 id', 'RelativeContainer 里要互相对齐,组件就要有稳定 id。没有 id 的组件,别人就没法拿它当锚点。')

          RelativeContainer() {
            Text('顶部居中')
              .height(36)
              .padding({ left: 12, right: 12 })
              .fontSize(13)
              .fontWeight(700)
              .fontColor('#FFFFFF')
              .textAlign(TextAlign.Center)
              .backgroundColor('#2563EB')
              .borderRadius(8)
              .id('topBadge')
              .alignRules({
                top: { anchor: '__container__', align: VerticalAlign.Top },
                middle: { anchor: '__container__', align: HorizontalAlign.Center }
              })

            Text('左侧')
              .height(36)
              .padding({ left: 12, right: 12 })
              .fontSize(13)
              .fontWeight(700)
              .fontColor('#FFFFFF')
              .textAlign(TextAlign.Center)
              .backgroundColor('#0F766E')
              .borderRadius(8)
              .id('leftBadge')
              .alignRules({
                left: { anchor: '__container__', align: HorizontalAlign.Start },
                center: { anchor: '__container__', align: VerticalAlign.Center }
              })

            Text('右侧')
              .height(36)
              .padding({ left: 12, right: 12 })
              .fontSize(13)
              .fontWeight(700)
              .fontColor('#FFFFFF')
              .textAlign(TextAlign.Center)
              .backgroundColor('#C2410C')
              .borderRadius(8)
              .id('rightBadge')
              .alignRules({
                right: { anchor: '__container__', align: HorizontalAlign.End },
                center: { anchor: '__container__', align: VerticalAlign.Center }
              })

            Text('底部居中')
              .height(36)
              .padding({ left: 12, right: 12 })
              .fontSize(13)
              .fontWeight(700)
              .fontColor('#FFFFFF')
              .textAlign(TextAlign.Center)
              .backgroundColor('#7C3AED')
              .borderRadius(8)
              .id('bottomBadge')
              .alignRules({
                bottom: { anchor: '__container__', align: VerticalAlign.Bottom },
                middle: { anchor: '__container__', align: HorizontalAlign.Center }
              })
          }
          .width('100%')
          .height(220)
          .padding(12)
          .backgroundColor('#FFFFFF')
          .border({ width: 1, color: '#DBEAFE' })
          .borderRadius(8)
        }
        .width('100%')
        .padding(16)
        .backgroundColor('#EFF6FF')
        .borderRadius(8)

        Column({ space: 12 }) {
          this.sectionTitle('兄弟组件也可以互相当锚点', '第二个组件不一定非得对齐容器,也可以贴着第一个组件的右边或下边走。复杂信息卡会经常用到。')

          RelativeContainer() {
            Text('A')
              .width(56)
              .height(56)
              .fontSize(18)
              .fontWeight(700)
              .fontColor('#FFFFFF')
              .textAlign(TextAlign.Center)
              .backgroundColor('#2563EB')
              .borderRadius(8)
              .id('cardA')
              .alignRules({
                left: { anchor: '__container__', align: HorizontalAlign.Start },
                top: { anchor: '__container__', align: VerticalAlign.Top }
              })

            Text('B 贴在 A 右侧')
              .height(56)
              .padding({ left: 12, right: 12 })
              .fontSize(13)
              .fontWeight(700)
              .fontColor('#FFFFFF')
              .textAlign(TextAlign.Center)
              .backgroundColor('#0F766E')
              .borderRadius(8)
              .id('cardB')
              .alignRules({
                left: { anchor: 'cardA', align: HorizontalAlign.End },
                top: { anchor: 'cardA', align: VerticalAlign.Top }
              })
              .margin({ left: 10 })

            Text('C 贴在 A 下方')
              .width(146)
              .height(44)
              .fontSize(13)
              .fontWeight(700)
              .fontColor('#FFFFFF')
              .textAlign(TextAlign.Center)
              .backgroundColor('#7C3AED')
              .borderRadius(8)
              .id('cardC')
              .alignRules({
                top: { anchor: 'cardA', align: VerticalAlign.Bottom },
                left: { anchor: 'cardA', align: HorizontalAlign.Start }
              })
              .margin({ top: 10 })
          }
          .width('100%')
          .height(130)
          .padding(12)
          .backgroundColor('#FFFFFF')
          .border({ width: 1, color: '#CCFBF1' })
          .borderRadius(8)
        }
        .width('100%')
        .padding(16)
        .backgroundColor('#F0FDFA')
        .borderRadius(8)

        Column({ space: 12 }) {
          this.sectionTitle('辅助线和屏障适合统一对齐', 'guideLine 可以提供虚拟锚点。barrier 可以根据一组组件的边界生成参考线,避免后面的内容压上来。')

          RelativeContainer() {
            Text('左侧信息')
              .width(112)
              .height(54)
              .fontSize(13)
              .fontWeight(700)
              .fontColor('#FFFFFF')
              .textAlign(TextAlign.Center)
              .backgroundColor('#C2410C')
              .borderRadius(8)
              .id('leftInfo')
              .alignRules({
                right: { anchor: 'middleLine', align: HorizontalAlign.Start },
                top: { anchor: '__container__', align: VerticalAlign.Top }
              })
              .margin({ right: 8 })

            Text('右侧信息更高')
              .width(128)
              .height(72)
              .fontSize(13)
              .fontWeight(700)
              .fontColor('#FFFFFF')
              .textAlign(TextAlign.Center)
              .backgroundColor('#1D4ED8')
              .borderRadius(8)
              .id('rightInfo')
              .alignRules({
                left: { anchor: 'middleLine', align: HorizontalAlign.End },
                top: { anchor: '__container__', align: VerticalAlign.Top }
              })
              .margin({ left: 8 })

            Text('跟在屏障下面')
              .width('100%')
              .height(42)
              .fontSize(13)
              .fontWeight(700)
              .fontColor('#334155')
              .textAlign(TextAlign.Center)
              .backgroundColor('#F1F5F9')
              .borderRadius(8)
              .alignRules({
                top: { anchor: 'bottomBarrier', align: VerticalAlign.Bottom },
                left: { anchor: '__container__', align: HorizontalAlign.Start }
              })
              .margin({ top: 12 })
          }
          .guideLine([
            { id: 'middleLine', direction: Axis.Vertical, position: { start: '50%' } }
          ])
          .barrier([
            { id: 'bottomBarrier', direction: BarrierDirection.BOTTOM, referencedId: ['leftInfo', 'rightInfo'] }
          ])
          .width('100%')
          .height(150)
          .padding(12)
          .backgroundColor('#FFFFFF')
          .border({ width: 1, color: '#FED7AA' })
          .borderRadius(8)
        }
        .width('100%')
        .padding(16)
        .backgroundColor('#FFF7ED')
        .borderRadius(8)
      }
      .width('100%')
      .padding(20)
      .alignItems(HorizontalAlign.Start)
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#FFFFFF')
  }
}
【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。