鸿蒙应用开发UI基础第十三节:RelativeContainer相对布局优势与实战演示

【学习目标】

  1. 理解 RelativeContainer 的核心定位与扁平化优势,掌握「参考边界 - 锚点 - 对齐方式」三大核心概念;
  2. 精通基于父容器、兄弟组件的基础定位用法,熟练配置 alignRules 核心属性;
  3. 掌握组件偏移微调、多组件联动对齐、链式布局等进阶能力,夯实复杂界面布局开发基础;
  4. 掌握辅助线(Guideline)、屏障(Barrier)等进阶能力,适配更复杂的对齐场景;
  5. 独立搭建专属工程,通过基础示例与实战案例落地核心用法,解决传统布局嵌套冗余、适配性差的问题。

一、RelativeContainer 核心基础

1.1 核心定位与核心优势

RelativeContainer 是为解决复杂界面多层嵌套性能问题设计的相对布局容器,支持容器内子组件通过「锚点+参考边界+对齐方式」设置相对位置关系,适用于处理界面复杂、多元素需要精准对齐排列的场景。

其核心优势体现在两方面:

  • 性能优化:扁平化布局替代多层线性布局嵌套,减少布局层级与渲染性能开销;
  • 灵活定位:子组件可指定父容器、兄弟组件、辅助线、屏障作为锚点,适配各类复杂对齐场景。

1.2 核心基础概念

所有布局能力均基于以下核心概念,是理解 RelativeContainer 的关键:

  1. 参考边界 :设置当前组件的哪个边界对齐到锚点,分水平和垂直方向:
    • 水平方向:起始(left)、居中(middle)、尾端(right);
    • 垂直方向:顶部(top)、居中(center)、底部(bottom)。
      生效规则:
    • 水平方向同时设置三个边界时,仅**起始(left)、居中(middle)**生效,尾端(right)配置会被忽略;
    • 垂直方向同时设置三个边界时,仅**顶部(top)、居中(center)**生效,底部(bottom)会被忽略。
  2. 锚点 :组件定位的参考对象,支持四类核心类型:
    • 父容器:默认固定标识为 __container__
    • 兄弟组件:需设置唯一 id 作为引用标识;
    • 辅助线(Guideline):虚拟参考线,用于统一对齐到特定偏移位置;
    • 屏障(Barrier):基于一组组件边界生成的动态锚点。
  3. 对齐方式 :组件参考边界与锚点的匹配规则:
    • 水平方向:HorizontalAlign.Start/Center/End
    • 垂直方向:VerticalAlign.Top/Center/Bottom
  4. 链(Chain) :将一系列组件首尾相连形成的链式结构,通过 chainMode 可指定链内元素的排列方式。
  5. 辅助线(Guideline):容器内虚拟的水平/垂直锚点,便于批量组件统一对齐到特定偏移位置。
  6. 屏障(Barrier):一组指定组件在特定方向上的公共最远边界(如多个组件底部的最下边界),作为动态锚点使用。

1.3 核心示意图

依赖关系示意图 (展示组件锚点依赖逻辑) 设置参考边界示意图 (展示参考边界配置规则)

1.4 核心适用场景

  • 复杂表单:多元素不规则对齐,避免 Column/Row 多层嵌套;
  • 卡片式布局:多组件联动定位,适配动态内容;
  • 不规则元素排布:商品详情页、个性化主页等需要精准对齐的场景;
  • 批量组件统一对齐:需辅助线/屏障实现的批量对齐场景。

1.5 与基础布局的核心区别

布局容器 核心定位逻辑 嵌套性 核心适用场景 核心短板 性能评级
Column 垂直顺序排布 易嵌套 纯垂直单方向的简单布局 复杂页面需多层嵌套
Row 水平顺序排布 易嵌套 纯水平单方向的简单布局 复杂页面需多层嵌套
Stack Z轴层叠排布 低嵌套 多组件同一空间层叠(角标) 无法实现平面多元素排布
RelativeContainer 锚点+参考边界+对齐方式定位 无嵌套 复杂页面的多元素灵活排布 简单单方向排布效率低于线性布局 中高

1.6 核心选型原则

  • 纯垂直/纯水平单方向排布 → 用 Column/Row(简单高效);
  • 多组件同一空间层叠 → 用 Stack(唯一选择);
  • 多元素复杂排布、易产生深层嵌套 → 优先用 RelativeContainer(扁平化+性能优化);
  • 批量组件需统一偏移对齐 → 用 RelativeContainer + 辅助线/屏障。

1.7 重要注意事项

  1. 锚点标识规则
    • 未设置 id 的组件可显示,但无法被引用为锚点;
    • 辅助线/屏障的 id 需唯一,避免与组件冲突,优先级:组件 > guideline > barrier。
  2. 依赖循环约束
    • 非链布局禁止组件循环依赖(A→B、B→A),会导致组件无法绘制;
    • 链布局通过 chainMode 声明后,容器自动解析链式循环依赖(唯一允许循环依赖的场景)。
  3. 尺寸渲染规则
    • API Version 11及以后,组件自身 width/height 优先级高于布局规则;若要使子组件与锚点严格对齐,应仅使用 alignRules,避免使用尺寸设置。
    • 若无法通过约束条件+自身size确定组件大小,组件将不绘制;
    • 同一方向设置多个锚点时,若锚点位置顺序错误,组件视为尺寸0不绘制。
  4. 偏移优先级
    • offset 为像素级偏移,当使用 offset 调整位置的组件作为锚点时,对齐位置为设置 offset 之前的位置;
    • 从 API Version 11 开始新增 Bias 对象,建议 API 11 及以后使用 bias 设置额外偏移
    • bias 为比例偏移,取值 0--1;
    • 链内元素的 bias 属性全部失效,仅链头的 bias 作为整链的偏移生效。

二、核心属性与配置规则

2.1 标准配置格式

javascript 复制代码
// 1. 父容器作为锚点(最基础的定位方式,依赖容器自身边界)
alignRules: {
  // 垂直方向:当前组件的顶部边界 → 对齐到父容器(__container__为父容器固定标识)的顶部
  top: { anchor: "__container__", align: VerticalAlign.Top },
  // 水平方向:当前组件的左侧边界 → 对齐到父容器的起始侧
  left: { anchor: "__container__", align: HorizontalAlign.Start }
}

// 2. 兄弟组件作为锚点(组件间联动定位,需先给兄弟组件设置唯一id)
alignRules: {
  // 垂直方向:当前组件的顶部边界 → 对齐到id为siblingId的兄弟组件的底部
  top: { anchor: "siblingId", align: VerticalAlign.Bottom }, 
  // 水平方向:当前组件的左侧边界 → 对齐到id为siblingId的兄弟组件的起始侧
  left: { anchor: "siblingId", align: HorizontalAlign.Start }
}

// 3. 辅助线/屏障作为锚点(批量组件统一对齐场景,需先定义辅助线/屏障并配置id)
alignRules: {
  // 水平方向:当前组件的左侧边界 → 对齐到id为guideline1的辅助线的结束侧
  left: { anchor: "guideline1", align: HorizontalAlign.End },
  // 垂直方向:当前组件的顶部边界 → 对齐到id为barrier1的屏障的底部
  top: { anchor: "barrier1", align: VerticalAlign.Bottom }
}

// 居中对齐配置(父容器锚点)
alignRules: {
  // 水平方向:当前组件的水平中轴 → 对齐到父容器的水平中轴
  middle: { anchor: "__container__", align: HorizontalAlign.Center },
  // 垂直方向:当前组件的垂直中轴 → 对齐到父容器的垂直中轴
  center: { anchor: "__container__", align: VerticalAlign.Center }
}

// 混合锚点配置(水平参考父容器,垂直参考兄弟组件)
alignRules: {
  // 水平方向:当前组件的水平中轴 → 对齐到父容器的水平中轴
  middle: { anchor: "__container__", align: HorizontalAlign.Center },
  // 垂直方向:当前组件的顶部边界 → 对齐到id为title的兄弟组件的底部
  top: { anchor: "title", align: VerticalAlign.Bottom },
  // 默认比例,水平居中、垂直居中 取值 0-1之间。
  bias: { horizontal: 0.5, vertical: 0.5 }
}

2.2 硬性约束

  1. 锚点标识唯一:同一容器内所有 id(组件/辅助线/屏障)不可重复;
  2. 至少一个参考边界:alignRules 必须配置至少一个边界,否则相对定位失效;
  3. 锚点定义顺序:兄弟组件定位时,锚点组件必须先定义,依赖组件后定义;
  4. 链布局尺寸约束:若链内元素总尺寸超出锚点约束范围,超出部分会均匀分配到链的两侧(PACKED链可通过bias调整超出部分的分布);
  5. 屏障使用规则:
    • 垂直屏障(TOP/BOTTOM)仅能作为水平方向锚点,用作垂直锚点时值为0;
    • 水平屏障(LEFT/RIGHT)仅能作为垂直方向锚点,用作水平锚点时值为0。

2.3 辅助属性

属性名 类型 核心作用 适用场景 注意事项
id string 唯一标识 所有需要被引用的组件/辅助线/屏障 必须唯一,优先级:组件 > guideline > barrier
offset 像素偏移 精准位置校准、固定间距调整 作为锚点时,取调整前的原始位置
bias 比例偏移 适配性微调、响应式布局 链内元素bias失效,仅链头bias生效
chainMode (direction: Axis, style: ChainStyle) 链式排布 多组件链式排布 仅链头配置,作用于整链;链头为水平链左侧(镜像语言右侧)/垂直链上方组件

2.4 核心枚举

(1)HorizontalAlign(水平对齐方式)

  • HorizontalAlign.Start:与锚点起始侧(左侧)对齐;
  • HorizontalAlign.Center:与锚点水平居中对齐;
  • HorizontalAlign.End:与锚点尾端(右侧)对齐。

(2)VerticalAlign(垂直对齐方式)

  • VerticalAlign.Top:与锚点顶部对齐;
  • VerticalAlign.Center:与锚点垂直居中对齐;
  • VerticalAlign.Bottom:与锚点底部对齐。

(3)ChainStyle(链布局样式)

  • ChainStyle.SPREAD:链内组件占满空间,剩余空间均分至组件间隙;
  • ChainStyle.SPREAD_INSIDE:首尾组件贴锚点边界,中间组件均匀分布;
  • ChainStyle.PACKED:组件无间隙紧密贴合,整体靠起始方向排列;

(4)Axis(链布局方向)

  • Axis.Horizontal:水平链式排布(最常用);
  • Axis.Vertical:垂直链式排布。

(5)BarrierDirection(屏障方向)

  • BarrierDirection.LEFT:左侧屏障(一组组件的最左边界);
  • BarrierDirection.RIGHT:右侧屏障(一组组件的最右边界);
  • BarrierDirection.TOP:顶部屏障(一组组件的最上边界);
  • BarrierDirection.BOTTOM:底部屏障(一组组件的最下边界)。

2.5 辅助线与屏障

(1)辅助线(Guideline)

核心作用 :虚拟参考线,批量组件统一对齐到特定偏移位置,避免重复设置偏移。
配置方式

javascript 复制代码
RelativeContainer() {
    // 其他元素
  // 子组件配置:引用辅助线作为锚点
  Text("参考辅助线对齐")
    .alignRules({
      left: { anchor: "vGuide1", align: HorizontalAlign.End },
      top: { anchor: "hGuide1", align: VerticalAlign.Top }
    })
    .id("text1")
}
// 辅助线配置(容器属性)
    .guideLine([
    {
        id: "vGuide1", // 垂直辅助线唯一标识
        direction: Axis.Vertical, // 方向:垂直
        position: { start: 50 } // 距离父容器左侧50vp
    },
    {
        id: "hGuide1", // 水平辅助线唯一标识
        direction: Axis.Horizontal, // 方向:水平
        position: { start: 160 } // 距离容器顶部160vp
    }
  ])

约束

  • 辅助线的position属性支持start(距离容器起始侧偏移)和end(距离容器结束侧偏移),两者冲突时仅start生效;
  • 容器尺寸为 auto 时,仅支持 start 声明,不允许使用百分比

辅助线示意图

RelativeContainer组件从原点(0,0)坐标发出的两条虚拟辅助线,他们的焦点就是锚点。"参考辅助线对齐组件"的坐标起始点就是锚点。

如 RelativeContainer组件从0点x轴移动50个vp,从y轴0点移动160vp。

(2)屏障(Barrier)

核心作用 :基于一组组件的最远边界生成动态锚点,避免组件重叠,适配动态内容。
配置方式

javascript 复制代码
RelativeContainer() {
      //   ... 元素组件
     // 参考屏障对齐的示例文本
        Text("参考屏障对齐")
          .backgroundColor("#2ca9e0")
          .fontColor(Color.White)
          .fontSize(14)
          .padding(5)
          .alignRules({
            left: { anchor: "rightBarrier", align: HorizontalAlign.End }, // 贴右侧屏障右边界
            top: { anchor: "bottomBarrier", align: VerticalAlign.Bottom } // 贴底部屏障下边界
          })
          .id("text2")
      }
       // 屏障配置:基于元素1/2/3生成边界锚点
      .barrier([
        {
          id: "rightBarrier",
          direction: BarrierDirection.RIGHT,
          referencedId: ["element2", "element3"] // 右侧屏障:取 element2、element3 的最右边界	
        },
        {
          id: "bottomBarrier",
          direction: BarrierDirection.BOTTOM,
          referencedId: ["element1","element3"] // 底部屏障:取 element1、element3 的最下边界	
        }
      ])

约束

  • 垂直屏障(TOP/BOTTOM):只能用于水平方向定位(left/middle/right);
  • 水平屏障(LEFT/RIGHT):只能用于垂直方向定位(top/center/bottom);
  • 方向不匹配时,锚点值视为0,组件定位失效。

虚拟屏障示意图

虚拟屏障就是以某些元素组件的边缘(如元素1、元素3最下边,元素2和元素3的最右边)

三、工程结构

3.1 基础工程目录

复制代码
RelativeContainerApp/
├── AppScope/                # 应用全局配置目录
└── entry/                   # 主模块目录(核心代码)
    ├── src/
    │   ├── main/
    │   │   ├── ets/         # ETS代码核心目录
    │   │   │   ├── entryability/       # 应用入口能力(启动配置)
    │   │   │   ├── entrybackupability/ # 备份能力(可选)
    │   │   │   ├── pages/             # 页面存放目录(核心)
    │   │   │   │   ├── BasicParent.ets      # 4.1 基于父容器定位示例
    │   │   │   │   ├── BasicSibling.ets     # 4.2 基于兄弟组件定位示例
    │   │   │   │   ├── ChainLayout.ets      # 4.3 链式布局示例
    │   │   │   │   ├── GuidelineBarrier.ets # 4.4 辅助线+屏障示例
    │   │   │   │   ├── Index.ets            # 工程默认首页
    │   │   │   │   ├── LoginForm.ets     # 5.2 登录表单实战案例
    │   │   │   └── resources/          # 资源目录(图片/配置等)
    │   │   │       ├── media/          # 媒体资源(如logo图片、运行效果截图)
    │   │   │       └── rawfile/        # 原始文件(可选)
    │   │   ├── module.json5            # 模块配置文件(声明页面路由、权限等)
    │   ├── mock/            # 模拟数据目录(可选)
    │   ├── ohosTest/        # OHOS单元测试目录(可选)
    │   ├── test/            # 通用测试目录(可选)
    ├── .gitignore           # Git忽略配置
    ├── build-profile.json5  # 构建配置(编译选项、API版本等)
    └── hvigorfile.ts        # 构建工具配置

3.2 能力演示首页

javascript 复制代码
import { router } from '@kit.ArkUI';

// 1.数据接口
interface NavButtonItem {
  title: string; // 按钮文字
  path: string; // 跳转页面路径
}

@Entry
@Component
struct Index {
  // 2. 定义按钮数据源
  private navButtons: NavButtonItem[] = [
    { title: "1. 基础能力:基于父容器定位", path: "pages/BasicParent" },
    { title: "2. 基础能力:基于兄弟组件定位", path: "pages/BasicSibling" },
    { title: "3. 进阶能力:多组件链布局", path: "pages/ChainLayout" },
    { title: "4. 进阶能力:辅助线+屏障", path: "pages/GuidelineBarrier" },
    { title: "5. 实战案例:扁平化登录表单", path: "pages/LoginForm" }
  ];

  build() {
    Column({ space: 25 }) {
      // 标题
      Text("RelativeContainer 核心教程")
        .fontSize(30)
        .fontWeight(FontWeight.Bold)
        .margin({ top: 80, bottom: 40 });

      // 3. ForEach循环生成按钮
      ForEach(
        this.navButtons,
        (item: NavButtonItem) => {
          Button(item.title)
            .width('85%')
            .height(50)
            .backgroundColor("#1890FF")
            .fontColor(Color.White)
            .borderRadius(8)
            .onClick(() => router.pushUrl({ url: item.path }));
        },
        // 唯一key生成:基于title+path拼接
        (item: NavButtonItem) => `${item.title}-${item.path}`
      );
    }
    .width('100%')
    .height('100%')
    .backgroundColor("#F5F5F5");
  }
}

运行效果

四、基础能力示例

4.1 基于父容器定位(BasicParent.ets)

javascript 复制代码
@Entry
@Component
struct BasicParent {
  build() {
    Column() {
      Text("基于父容器定位")
        .fontSize(20)
        .fontWeight(FontWeight.Bold)
        .margin({ bottom: 20 })

      RelativeContainer() {
        // 左上角组件
        Text("左上角")
          .backgroundColor("#a3cf62")
          .padding(10)
          .alignRules({
            top: { anchor: "__container__", align: VerticalAlign.Top },
            left: { anchor: "__container__", align: HorizontalAlign.Start }
          })
          .id("text1")

        // 右上角组件
        Text("右上角")
          .backgroundColor("#00ae9d")
          .padding(10)
          .alignRules({
            top: { anchor: "__container__", align: VerticalAlign.Top },
            right: { anchor: "__container__", align: HorizontalAlign.End }
          })
          .id("text2")

        // 居中组件
        Text("居中")
          .backgroundColor("#0a59f7")
          .padding(10)
          .alignRules({
            middle: { anchor: "__container__", align: HorizontalAlign.Center },
            center: { anchor: "__container__", align: VerticalAlign.Center }
          })
          .id("text3")

        // 底部居中组件
        Text("底部居中")
          .backgroundColor("#2ca9e0")
          .padding(10)
          .alignRules({
            middle: { anchor: "__container__", align: HorizontalAlign.Center },
            bottom: { anchor: "__container__", align: VerticalAlign.Bottom }
          })
          .id("text4")
      }
      .width(300)
      .height(300)
      .border({ width: 2, color: "#6699FF" })
    }
    .width("100%")
    .height("100%")
    .justifyContent(FlexAlign.Center)
    .padding(20)
  }
}

运行效果

4.2 基于兄弟组件定位(BasicSibling.ets)

javascript 复制代码
@Entry
@Component
struct BasicSibling {
  build() {
    Column() {
      Text("基于兄弟组件定位")
        .fontSize(20)
        .fontWeight(FontWeight.Bold)
        .margin({ bottom: 20 })

      RelativeContainer() {
        // 基准组件
        Text("基准组件")
          .backgroundColor("#a3cf62")
          .padding(10)
          .width(100)
          .height(60)
          .alignRules({
            top: { anchor: "__container__", align: VerticalAlign.Top },
            left: { anchor: "__container__", align: HorizontalAlign.Start }
          })
          .id("base")

        // 基准组件右侧
        Text("右侧")
          .backgroundColor("#00ae9d")
          .padding(10)
          .alignRules({
            top: { anchor: "base", align: VerticalAlign.Top },
            left: { anchor: "base", align: HorizontalAlign.End },
            bottom: { anchor: "base", align: VerticalAlign.Bottom }
          })
          .id("right")

        // 基准组件下方
        Text("下方")
          .backgroundColor("#0a59f7")
          .padding(10)
          .alignRules({
            top: { anchor: "base", align: VerticalAlign.Bottom },
            left: { anchor: "base", align: HorizontalAlign.Start },
            right: { anchor: "right", align: HorizontalAlign.End }
          })
          .id("bottom")

        // 基准组件右下角
        Text("右下角")
          .backgroundColor("#2ca9e0")
          .padding(10)
          .alignRules({
            top: { anchor: "bottom", align: VerticalAlign.Top },
            left: { anchor: "right", align: HorizontalAlign.Start },
            bottom: { anchor: "bottom", align: VerticalAlign.Bottom },
            right: { anchor: "__container__", align: HorizontalAlign.End }
          })
          .id("rightBottom")
      }
      .width(300)
      .height(200)
      .border({ width: 2, color: "#6699FF" })
    }
    .width("100%")
    .height("100%")
    .justifyContent(FlexAlign.Center)
    .padding(20)
  }
}

运行效果

4.3 链式布局(ChainLayout.ets)

javascript 复制代码
@Entry
@Component
struct ChainLayout {
  build() {
    Column() {
      Text("链式布局示例")
        .fontSize(20)
        .fontWeight(FontWeight.Bold)
        .margin({ bottom: 20 })

      RelativeContainer() {
        // SPREAD链 - 均匀分布
        Text("SPREAD-1")
          .backgroundColor("#a3cf62")
          .padding(10)
          .alignRules({
            left: { anchor: "__container__", align: HorizontalAlign.Start },
            right: { anchor: "spread2", align: HorizontalAlign.Start },
            top: { anchor: "__container__", align: VerticalAlign.Top }
          })
          .id("spread1")
          .chainMode(Axis.Horizontal, ChainStyle.SPREAD)

        Text("SPREAD-2")
          .backgroundColor("#00ae9d")
          .padding(10)
          .alignRules({
            left: { anchor: "spread1", align: HorizontalAlign.End },
            right: { anchor: "spread3", align: HorizontalAlign.Start },
            top: { anchor: "spread1", align: VerticalAlign.Top }
          })
          .id("spread2")

        Text("SPREAD-3")
          .backgroundColor("#0a59f7")
          .padding(10)
          .alignRules({
            left: { anchor: "spread2", align: HorizontalAlign.End },
            right: { anchor: "__container__", align: HorizontalAlign.End },
            top: { anchor: "spread1", align: VerticalAlign.Top }
          })
          .id("spread3")

        // SPREAD_INSIDE链 - 首尾贴边
        Text("INSIDE-1")
          .backgroundColor("#a3cf62")
          .padding(10)
          .alignRules({
            left: { anchor: "__container__", align: HorizontalAlign.Start },
            right: { anchor: "inside2", align: HorizontalAlign.Start },
            center: { anchor: "__container__", align: VerticalAlign.Center }
          })
          .id("inside1")
          .chainMode(Axis.Horizontal, ChainStyle.SPREAD_INSIDE)

        Text("INSIDE-2")
          .backgroundColor("#00ae9d")
          .padding(10)
          .alignRules({
            left: { anchor: "inside1", align: HorizontalAlign.End },
            right: { anchor: "inside3", align: HorizontalAlign.Start },
            top: { anchor: "inside1", align: VerticalAlign.Top }
          })
          .id("inside2")

        Text("INSIDE-3")
          .backgroundColor("#0a59f7")
          .padding(10)
          .alignRules({
            left: { anchor: "inside2", align: HorizontalAlign.End },
            right: { anchor: "__container__", align: HorizontalAlign.End },
            top: { anchor: "inside1", align: VerticalAlign.Top }
          })
          .id("inside3")

        // PACKED链 - 紧密贴合
        Text("PACKED-1")
          .backgroundColor("#a3cf62")
          .padding(10)
          .alignRules({
            left: { anchor: "__container__", align: HorizontalAlign.Start },
            right: { anchor: "packed2", align: HorizontalAlign.Start },
            bottom: { anchor: "__container__", align: VerticalAlign.Bottom }
          })
          .id("packed1")
          .chainMode(Axis.Horizontal, ChainStyle.PACKED)

        Text("PACKED-2")
          .backgroundColor("#00ae9d")
          .padding(10)
          .alignRules({
            left: { anchor: "packed1", align: HorizontalAlign.End },
            right: { anchor: "packed3", align: HorizontalAlign.Start },
            top: { anchor: "packed1", align: VerticalAlign.Top }
          })
          .id("packed2")

        Text("PACKED-3")
          .backgroundColor("#0a59f7")
          .padding(10)
          .alignRules({
            left: { anchor: "packed2", align: HorizontalAlign.End },
            right: { anchor: "__container__", align: HorizontalAlign.End },
            top: { anchor: "packed1", align: VerticalAlign.Top }
          })
          .id("packed3")
      }
      .width(300)
      .height(300)
      .border({ width: 2, color: "#6699FF" })
    }
    .width("100%")
    .height("100%")
    .justifyContent(FlexAlign.Center)
    .padding(20)
  }
}

运行结果

4.4 辅助线+屏障示例(GuidelineBarrier.ets)

javascript 复制代码
@Entry
@Component
struct GuidelineBarrier {
  build() {
    Column() {
      Text("基于辅助线+屏障的不规则布局")
        .fontSize(20)
        .fontWeight(FontWeight.Bold)
        .margin({ bottom: 20 })

      // 核心RelativeContainer布局
      RelativeContainer() {
        // 元素1:左上角区域
        Text("元素1")
          .backgroundColor("#1879e6")
          .fontColor(Color.White)
          .fontSize(18)
          .textAlign(TextAlign.Center)
          .width(100)
          .height(120)
          .alignRules({
            top: { anchor: "__container__", align: VerticalAlign.Top },
            left: { anchor: "__container__", align: HorizontalAlign.Start }
          })
          .id("element1")

        Text("元素2")
          .backgroundColor("#1879e6")
          .fontColor(Color.White)
          .fontSize(18)
          .textAlign(TextAlign.Center)
          .width(100)
          .height(80)
          .alignRules({
            top: { anchor: "element1", align: VerticalAlign.Top },
            left: { anchor: "element1", align: HorizontalAlign.End }
          })
          .id("element2")

        Text("元素3")
          .backgroundColor("#1879e6")
          .fontColor(Color.White)
          .fontSize(18)
          .textAlign(TextAlign.Center)
          .width(80)
          .height(70)
          .alignRules({
            top: { anchor: "element2", align: VerticalAlign.Bottom },
            right: { anchor: "element2", align: HorizontalAlign.End },
          })
          .margin({top:10})
          .id("element3")

        // 参考屏障对齐的示例文本
        Text("参考屏障对齐")
          .backgroundColor("#2ca9e0")
          .fontColor(Color.White)
          .fontSize(14)
          .padding(5)
          .alignRules({
            left: { anchor: "rightBarrier", align: HorizontalAlign.End }, // 贴右侧屏障右边界
            top: { anchor: "bottomBarrier", align: VerticalAlign.Bottom } // 贴底部屏障下边界
          })
          .id("text2")

        // 参考屏障对齐的示例文本
        Text("参考辅助线对齐")
          .backgroundColor("#2ca9e0")
          .fontColor(Color.White)
          .fontSize(14)
          .padding(5)
          .alignRules({
            left: { anchor: "vGuide1", align: HorizontalAlign.End },
            top: { anchor: "hGuide1", align: VerticalAlign.Top }
          })
          .id("text1")
      }
      // 屏障配置:基于元素1/2/3生成边界锚点
      .barrier([
        {
          id: "rightBarrier", // 右侧屏障(元素3的右边界)
          direction: BarrierDirection.RIGHT,
          referencedId: ["element2", "element3"] // 关联元素3/4,取最右边界
        },
        {
          id: "bottomBarrier", // 底部屏障(元素元素1/3的最下边界)
          direction: BarrierDirection.BOTTOM,
          referencedId: ["element1","element3"] // 关联元素1/3,取最下边界
        }
      ])
      // 辅助线配置(容器属性)
      .guideLine([
        {
          id: "vGuide1", // 垂直辅助线唯一标识
          direction: Axis.Vertical, // 方向:垂直
          position: { start: 50 } // 距离父容器左侧50vp

        },
        {
          id: "hGuide1", // 水平辅助线唯一标识
          direction: Axis.Horizontal, // 方向:水平
          position: { start: 160 } // 距离容器顶部160vp
        }
      ])
      .width('100%') // 容器宽度适配元素总宽度
      .height(400) // 容器高度
      .backgroundColor("#a4c2f4") // 背景色区分容器区域
      .border({ width: 1, color: "#1879e6" })
    }
    .width("100%")
    .height("100%")
    .justifyContent(FlexAlign.Center)
    .padding(10)
  }
}

运行效果

五、实战案例:扁平化登录表单

5.1 案例效果

实现一个无嵌套的登录表单,包含logo、账号输入框、密码输入框、记住密码复选框、登录按钮、注册入口,所有元素精准对齐,无多层 Column/Row 嵌套。

5.2 完整代码(LoginForm.ets)

javascript 复制代码
@Entry
@Component
struct LoginForm {
  build() {
    // 唯一根容器:RelativeContainer,承载所有元素
    RelativeContainer() {
      // 页面标题
      Text("登录表单实战")
        .fontSize(24)
        .fontWeight(FontWeight.Bold)
        .alignRules({
          // 水平居中:相对于根容器水平居中
          middle: { anchor: "__container__", align: HorizontalAlign.Center },
          // 垂直定位:距离根容器顶部40vp
          top: { anchor: "__container__", align: VerticalAlign.Top }
        })
        .margin({ top: 40 })
        .id("title")

      // ========== 表单核心元素:直接基于根容器/兄弟元素定位 ==========
      // Logo
      Image($r("app.media.startIcon"))
        .width(80)
        .height(80)
        .alignRules({
          // 水平居中:相对于根容器水平居中
          middle: { anchor: "__container__", align: HorizontalAlign.Center },
          // 垂直定位:在标题下方,间距20vp
          top: { anchor: "title", align: VerticalAlign.Bottom }
        })
        .margin({ top: 20 })
        .id("logo")

      // 账号输入框
      TextInput({ placeholder: "请输入账号" })
        .backgroundColor("#f5f5f5")
        .padding(10)
        .borderRadius(8)
        .alignRules({
          // 水平:左右边距20vp,相对于根容器左右边界
          left: { anchor: "__container__", align: HorizontalAlign.Start },
          right: { anchor: "__container__", align: HorizontalAlign.End },
          // 垂直:在Logo下方,间距30vp
          top: { anchor: "logo", align: VerticalAlign.Bottom }
        })
        .margin({ top: 30})
        .id("accountInput")

      // 密码输入框
      TextInput({ placeholder: "请输入密码" })
        .type(InputType.Password)
        .backgroundColor("#f5f5f5")
        .padding(10)
        .borderRadius(8)
        .alignRules({
          // 水平:与账号输入框左右对齐
          left: { anchor: "accountInput", align: HorizontalAlign.Start },
          right: { anchor: "accountInput", align: HorizontalAlign.End },
          // 垂直:在账号输入框下方,间距15vp
          top: { anchor: "accountInput", align: VerticalAlign.Bottom }
        })
        .margin({ top: 15 })
        .id("pwdInput")

      // 记住密码复选框
      Checkbox()
        .select(false)
        .alignRules({
          // 水平:与账号输入框左对齐
          left: { anchor: "accountInput", align: HorizontalAlign.Start },
          // 垂直:在密码输入框下方,间距15vp
          top: { anchor: "pwdInput", align: VerticalAlign.Bottom }
        })
        .margin({ top: 15 })
        .id("checkbox")

      // 记住密码文字
      Text("记住密码")
        .fontSize(14)
        .alignRules({
          // 水平:在复选框右侧,间距5vp
          left: { anchor: "checkbox", align: HorizontalAlign.End },
          // 垂直:与复选框垂直居中对齐
          center: { anchor: "checkbox", align: VerticalAlign.Center }
        })
        .margin({ left: 5 })
        .id("checkboxText")

      // 登录按钮
      Button("登录")
        .backgroundColor("#0a59f7")
        .fontColor(Color.White)
        .borderRadius(8)
        .alignRules({
          // 水平:与账号输入框左右对齐
          left: { anchor: "accountInput", align: HorizontalAlign.Start },
          right: { anchor: "accountInput", align: HorizontalAlign.End },
          // 垂直:在复选框下方,间距20vp
          top: { anchor: "checkbox", align: VerticalAlign.Bottom }
        })
        .margin({ top: 20 })
        .width("100%")
        .height(45)
        .id("loginBtn")

      // 注册入口
      Text("还没有账号?立即注册")
        .fontSize(14)
        .fontColor("#0a59f7")
        .alignRules({
          // 水平:相对于根容器水平居中
          middle: { anchor: "__container__", align: HorizontalAlign.Center },
          // 垂直:在登录按钮下方,间距20vp
          top: { anchor: "loginBtn", align: VerticalAlign.Bottom }
        })
        .margin({ top: 20 })
        .id("registerText")
    }
    .width("100%")
    .height("100%")
    .padding(20)
  }
}

运行效果

六、内容总结

  1. 核心定位:RelativeContainer 是解决复杂界面多层嵌套性能问题的核心布局容器,通过「锚点+参考边界+对齐方式」实现扁平化布局;
  2. 核心概念:六大核心概念(参考边界、锚点、对齐方式、链、辅助线、屏障)覆盖基础到进阶能力,需熟练掌握参考边界生效规则、锚点优先级、链布局特殊约束等关键细节;
  3. 核心规则:锚点唯一、顺序约束、尺寸优先级(API 11+)、渲染规则是布局生效的关键,链布局和辅助线/屏障的方向/偏移约束是进阶使用的核心;
  4. 进阶能力:辅助线适配批量组件统一偏移对齐,屏障适配动态内容边界对齐,链布局实现多组件均匀/紧密排列,是复杂场景的核心解决方案;
  5. 实战价值:扁平化布局减少性能开销,灵活定位适配复杂界面,是复杂页面开发的最优选择。

七、代码仓库

八、下节预告

下一节我们将学习核心基础组件(一)文本显示组件 Text,从三大核心维度系统掌握文本展示全场景开发能力:

  1. 基础认知:明确Text组件只读展示的核心定位,掌握静态文本、多格式混排、长文本等典型场景的应用要点;
  2. 核心能力:掌握Text基础样式(字体、颜色、装饰线)、Span图文混排、排版溢出(maxLines/textOverflow)、交互事件(onClick/copyOption)、自适应字号等核心属性及底层规则;
  3. 实战落地:基于TextApplication工程,实现热搜榜、可复制文本、自定义选择菜单等实战需求,解决文本样式定制、溢出处理、交互绑定等高频开发问题。