为一篇中文技术教程文章绘制一张手绘笔记风格的信息图,主题是“HarmonyOS7 RelativeC

前言

资料卡这种 UI,看起来不复杂,写着写着却很容易变成 ColumnRowRow 再套 Stack。一开始只是头像、昵称、简介,后面加关注按钮、标签、角标,层级马上就深了。

这时候可以考虑 RelativeContainer。它不是为了让代码变短,而是让元素之间的相对关系更清楚。

减少嵌套的目的不是省几行代码,而是让后面改位置时不用拆一堆容器。

为什么这个问题经常被写乱

RelativeContainer 减少嵌套 这类内容很容易被写成“代码能跑就算讲完了”,但对初学者来说,这恰恰是最不够的地方。真正让人卡住的,往往不是某个组件名记不住,而是不知道这段代码为什么要这样拆、状态为什么要这样放、以后需求变化时应该从哪里改。

所以这篇文章不只想给你一个能跑的例子,更想把背后的判断过程讲清楚。你只要把这个判断过程吃透,后面自己改页面、补需求、查问题时,心里会稳很多。

先判断是不是适合相对布局

不是所有卡片都应该换成 RelativeContainer。如果内容就是从上到下排列,Column 最直接;如果内容是横向排列,Row 也够用。

RelativeContainer 更适合那些“某个元素要对齐另一个元素”的场景。

场景 是否适合 原因
头像、昵称、按钮、标签混排 适合 元素位置关系明确
纯纵向表单 不适合 Column 更容易读
封面图右上角角标 适合 可以少套一层 Stack

为一篇讲解 HarmonyOS7 RelativeContainer 的中文文章绘制一张手绘笔记风格

| 动态瀑布流 | 不适合 | 更适合 GridList |

我自己的判断方式很简单:如果设计稿里你经常说“这个元素对齐头像右侧”“这个按钮贴容器右上角”,那就可以考虑相对布局。

这篇做一个资料卡

示例里的资料卡包含:

  • 左上角头像
  • 头像右侧姓名
  • 姓名下方简介
  • 右上角关注按钮
  • 头像下方技能标签

如果全用 RowColumn,也能写出来。但为了让右上按钮和底部标签的位置更直观,这里用 RelativeContainer 做外层。

先把页面目标想清楚

在真正写代码之前,先别急着盯着 API。更有用的做法是先想清楚:这个页面到底想解决什么问题,用户最在意的反馈是什么,哪些状态必须一直保持一致。

为一篇中文技术教程绘制一张手绘笔记风格流程图,主题是“写 RelativeContainer 之前先

当你先把这条主线想明白,再回头看组件和状态设计,很多选择都会顺理成章。对小白来说,这一步尤其重要,因为它能帮你从“照着抄”慢慢过渡到“看得懂、改得动”。

完整 ArkTS 示例

interface Skill29 {
  id: number
  name: string
}

@Entry
@Component
struct RelativeProfileCard29 {
  @State followed: boolean = false
  private skills: Skill29[] = [
    { id: 1, name: 'ArkUI' },
    { id: 2, name: 'ArkTS' },
    { id: 3, name: '性能优化' }
  ]

  build() {
    Column({ space: 16 }) {
      Text('资料卡')
        .fontSize(24)
        .fontWeight(FontWeight.Bold)
        .width('100%')

      RelativeContainer() {
        Stack() {
          Text('林')
            .fontSize(26)
            .fontColor('#FFFFFF')
            .fontWeight(FontWeight.Bold)
        }
        .id('avatar')
        .width(64)
        .height(64)
        .borderRadius(32)
        .backgroundColor('#2F6FED')
        .alignRules({
          left: { anchor: '__container__', align: HorizontalAlign.Start },
          top: { anchor: '__container__', align: VerticalAlign.Top }
        })

        Text('林澈')
          .id('name')
          .fontSize(22)
          .fontWeight(FontWeight.Bold)
          .alignRules({
            left: { anchor: 'avatar', align: HorizontalAlign.End },
            top: { anchor: 'avatar', align: VerticalAlign.Top }
          })
          .margin({ left: 14 })

        Text('HarmonyOS7 应用开发者')
          .id('desc')
          .fontSize(13)
          .fontColor('#666666')
          .alignRules({
            left: { anchor: 'name', align: HorizontalAlign.Start },
            top: { anchor: 'name', align: VerticalAlign.Bottom }
          })
          .margin({ top: 6 })

        Button(this.followed ? '已关注' : '关注')
          .id('action')
          .onClick(() => {
            this.followed = !this.followed
          })
          .alignRules({
            right: { anchor: '__container__', align: HorizontalAlign.End },
            top: { anchor: '__container__', align: VerticalAlign.Top }
          })

        Row({ space: 8 }) {
          ForEach(this.skills, (item: Skill29) => {
            Text(item.name)
              .fontSize(12)
              .fontColor('#2F6FED')
              .padding({ left: 10, right: 10, top: 5, bottom: 5 })
              .backgroundColor('#EAF1FF')
              .borderRadius(12)
          }, (item: Skill29) => item.id.toString())
        }
        .id('skills')
        .alignRules({
          left: { anchor: '__container__', align: HorizontalAlign.Start },
          top: { anchor: 'avatar', align: VerticalAlign.Bottom }
        })
        .margin({ top: 18 })
      }
      .width('100%')
      .height(150)
      .padding(16)
      .backgroundColor('#FFFFFF')
      .borderRadius(14)
    }
    .padding(16)
    .width('100%')
    .height('100%')
    .backgroundColor('#F6F7F9')
  }
}

id 是相对布局的锚点

RelativeContainer 里最重要的不是 alignRules(),而是前面的 .id()

头像设置了 .id('avatar'),姓名就可以说“我的左边对齐头像的右边”。姓名设置了 .id('name'),简介就可以说“我的顶部对齐姓名的底部”。

这种写法很接近设计稿语言。你不需要在脑子里还原三层 RowColumn,直接看规则就知道元素跟谁对齐。

__container__ 表示对齐父容器

示例里头像和按钮都对齐了 __container__。头像贴左上,按钮贴右上,这两个位置不依赖其他元素。

这类固定锚点很适合直接对齐容器。反过来,姓名和简介依赖头像与姓名,它们就应该对齐具体元素,而不是硬写一堆 margin

如果你发现一个元素的 margin 越写越多,通常说明它真正想表达的是相对关系,而不是单纯间距。

不是所有内容都要相对定位

示例里的技能标签仍然用了 Row + ForEach

这是有意的。标签本身是横向列表,内部元素之间是线性排列,Row 比相对规则更好读。RelativeContainer 负责把整组标签放到头像下面,标签内部怎么排,交给普通容器就行。

这点很重要。用 RelativeContainer 不是把所有子元素都平铺到一个容器里,否则代码会从“嵌套太多”变成“规则太乱”。

从嵌套布局迁移时怎么做

我一般不会看到嵌套多就立刻全改。先把元素关系画出来,再决定哪些节点适合作为锚点。

可以按这个顺序想:

  1. 找固定锚点,比如头像、右上按钮、底部标签区。
  2. 给锚点起稳定可读的 id,不要用 a1box2 这种临时名。
  3. 把姓名、描述这类文本绑定到头像或上一个文本。
  4. 局部线性内容继续用 RowColumn
  5. 最后看卡片高度是否能容纳动态内容。

这套流程比直接重写安全很多。你会知道每个元素为什么移动,而不是凭感觉挪位置。

相对布局和普通布局怎么取舍

判断点 继续用 Row/Column 改用 RelativeContainer
元素是否线性排列
是否有角标或悬浮按钮 不明显 明显
位置是否经常调整 很少 经常
是否是列表重复项 更常见 谨慎使用

列表项里也可以用 RelativeContainer,但要注意可读性。列表本来就会重复很多次,如果每个 item 的相对规则都很复杂,后期维护压力会变大。

容易踩坑的地方

id 重复是最常见的问题。相对规则都靠 id 找锚点,命名不清楚或者重复,排查会很费劲。

卡片高度太死也会出问题。示例里高度是 150,因为内容固定。如果标签变多、简介变长,就要考虑换行、最大行数或者让卡片高度自适应。

还有一个细节是按钮空间。右上角按钮看起来独立,但它可能压到姓名。真实项目里要给姓名留右侧空间,或者让文本限制最大宽度。

新手最容易踩的坑

这一类示例最容易让人产生错觉:界面出来了,就以为已经掌握了。其实真正容易出问题的地方,通常都在效果之外,比如状态有没有收拢、失败后怎么兜底、以后要扩展时会不会牵一发动全身。

所以你练这篇内容时,别只看“现在能不能跑”,还要继续看“以后好不好改”。能把这个习惯养起来,你写出来的页面会比单纯照着示例拼出来的页面稳很多。

放进真实项目还要补什么

示例代码的重点是把核心思路讲明白,所以很多工程化细节会故意省掉。真正落到项目里时,你通常还要继续补接口联动、异常处理、边界保护、资源抽离,以及和其他页面状态之间的配合。

比较稳的做法是分三步走:先把结构和职责立住,再把真实业务接进去,最后再优化视觉和交互体验。这样改出来的页面不只是“能演示”,而是真的更接近可以长期维护的业务代码。

写在最后

HarmonyOS7 页面里,真正该减少的是无意义嵌套。该用 Column 的地方继续用 Column,该表达相对关系的时候,再让 RelativeContainer 上场。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐