【学习目标】
- 理解 RelativeContainer 的核心定位与扁平化优势,掌握「参考边界 - 锚点 - 对齐方式」三大核心概念;
- 精通基于父容器、兄弟组件的基础定位用法,熟练配置 alignRules 核心属性;
- 掌握组件偏移微调、多组件联动对齐、链式布局等进阶能力,夯实复杂界面布局开发基础;
- 掌握辅助线(Guideline)、屏障(Barrier)等进阶能力,适配更复杂的对齐场景;
- 独立搭建专属工程,通过基础示例与实战案例落地核心用法,解决传统布局嵌套冗余、适配性差的问题。
一、RelativeContainer 核心基础
1.1 核心定位与核心优势
RelativeContainer 是为解决复杂界面多层嵌套性能问题设计的相对布局容器,支持容器内子组件通过「锚点+参考边界+对齐方式」设置相对位置关系,适用于处理界面复杂、多元素需要精准对齐排列的场景。
其核心优势体现在两方面:
- 性能优化:扁平化布局替代多层线性布局嵌套,减少布局层级与渲染性能开销;
- 灵活定位:子组件可指定父容器、兄弟组件、辅助线、屏障作为锚点,适配各类复杂对齐场景。
1.2 核心基础概念
所有布局能力均基于以下核心概念,是理解 RelativeContainer 的关键:
- 参考边界 :设置当前组件的哪个边界对齐到锚点,分水平和垂直方向:
- 水平方向:起始(left)、居中(middle)、尾端(right);
- 垂直方向:顶部(top)、居中(center)、底部(bottom)。
生效规则: - 水平方向同时设置三个边界时,仅**起始(left)、居中(middle)**生效,尾端(right)配置会被忽略;
- 垂直方向同时设置三个边界时,仅**顶部(top)、居中(center)**生效,底部(bottom)会被忽略。
- 锚点 :组件定位的参考对象,支持四类核心类型:
- 父容器:默认固定标识为
__container__; - 兄弟组件:需设置唯一
id作为引用标识; - 辅助线(Guideline):虚拟参考线,用于统一对齐到特定偏移位置;
- 屏障(Barrier):基于一组组件边界生成的动态锚点。
- 父容器:默认固定标识为
- 对齐方式 :组件参考边界与锚点的匹配规则:
- 水平方向:
HorizontalAlign.Start/Center/End; - 垂直方向:
VerticalAlign.Top/Center/Bottom。
- 水平方向:
- 链(Chain) :将一系列组件首尾相连形成的链式结构,通过
chainMode可指定链内元素的排列方式。 - 辅助线(Guideline):容器内虚拟的水平/垂直锚点,便于批量组件统一对齐到特定偏移位置。
- 屏障(Barrier):一组指定组件在特定方向上的公共最远边界(如多个组件底部的最下边界),作为动态锚点使用。
1.3 核心示意图
| 依赖关系示意图 (展示组件锚点依赖逻辑) | 设置参考边界示意图 (展示参考边界配置规则) |
|---|---|
1.4 核心适用场景
- 复杂表单:多元素不规则对齐,避免 Column/Row 多层嵌套;
- 卡片式布局:多组件联动定位,适配动态内容;
- 不规则元素排布:商品详情页、个性化主页等需要精准对齐的场景;
- 批量组件统一对齐:需辅助线/屏障实现的批量对齐场景。
1.5 与基础布局的核心区别
| 布局容器 | 核心定位逻辑 | 嵌套性 | 核心适用场景 | 核心短板 | 性能评级 |
|---|---|---|---|---|---|
| Column | 垂直顺序排布 | 易嵌套 | 纯垂直单方向的简单布局 | 复杂页面需多层嵌套 | 高 |
| Row | 水平顺序排布 | 易嵌套 | 纯水平单方向的简单布局 | 复杂页面需多层嵌套 | 高 |
| Stack | Z轴层叠排布 | 低嵌套 | 多组件同一空间层叠(角标) | 无法实现平面多元素排布 | 中 |
| RelativeContainer | 锚点+参考边界+对齐方式定位 | 无嵌套 | 复杂页面的多元素灵活排布 | 简单单方向排布效率低于线性布局 | 中高 |
1.6 核心选型原则
- 纯垂直/纯水平单方向排布 → 用 Column/Row(简单高效);
- 多组件同一空间层叠 → 用 Stack(唯一选择);
- 多元素复杂排布、易产生深层嵌套 → 优先用 RelativeContainer(扁平化+性能优化);
- 批量组件需统一偏移对齐 → 用 RelativeContainer + 辅助线/屏障。
1.7 重要注意事项
- 锚点标识规则 :
- 未设置
id的组件可显示,但无法被引用为锚点; - 辅助线/屏障的
id需唯一,避免与组件冲突,优先级:组件 > guideline > barrier。
- 未设置
- 依赖循环约束 :
- 非链布局禁止组件循环依赖(A→B、B→A),会导致组件无法绘制;
- 链布局通过
chainMode声明后,容器自动解析链式循环依赖(唯一允许循环依赖的场景)。
- 尺寸渲染规则 :
- API Version 11及以后,组件自身
width/height优先级高于布局规则;若要使子组件与锚点严格对齐,应仅使用 alignRules,避免使用尺寸设置。 - 若无法通过约束条件+自身size确定组件大小,组件将不绘制;
- 同一方向设置多个锚点时,若锚点位置顺序错误,组件视为尺寸0不绘制。
- API Version 11及以后,组件自身
- 偏移优先级 :
- 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 硬性约束
- 锚点标识唯一:同一容器内所有
id(组件/辅助线/屏障)不可重复; - 至少一个参考边界:
alignRules必须配置至少一个边界,否则相对定位失效; - 锚点定义顺序:兄弟组件定位时,锚点组件必须先定义,依赖组件后定义;
- 链布局尺寸约束:若链内元素总尺寸超出锚点约束范围,超出部分会均匀分配到链的两侧(PACKED链可通过bias调整超出部分的分布);
- 屏障使用规则:
- 垂直屏障(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)
}
}
运行效果
六、内容总结
- 核心定位:RelativeContainer 是解决复杂界面多层嵌套性能问题的核心布局容器,通过「锚点+参考边界+对齐方式」实现扁平化布局;
- 核心概念:六大核心概念(参考边界、锚点、对齐方式、链、辅助线、屏障)覆盖基础到进阶能力,需熟练掌握参考边界生效规则、锚点优先级、链布局特殊约束等关键细节;
- 核心规则:锚点唯一、顺序约束、尺寸优先级(API 11+)、渲染规则是布局生效的关键,链布局和辅助线/屏障的方向/偏移约束是进阶使用的核心;
- 进阶能力:辅助线适配批量组件统一偏移对齐,屏障适配动态内容边界对齐,链布局实现多组件均匀/紧密排列,是复杂场景的核心解决方案;
- 实战价值:扁平化布局减少性能开销,灵活定位适配复杂界面,是复杂页面开发的最优选择。
七、代码仓库
- 工程名称:RelativeContainerApp
- 仓库地址:https://gitee.com/HarmonyOS-UI-Basics/harmony-os-ui-basics.git
八、下节预告
下一节我们将学习核心基础组件(一)文本显示组件 Text,从三大核心维度系统掌握文本展示全场景开发能力:
- 基础认知:明确Text组件只读展示的核心定位,掌握静态文本、多格式混排、长文本等典型场景的应用要点;
- 核心能力:掌握Text基础样式(字体、颜色、装饰线)、Span图文混排、排版溢出(maxLines/textOverflow)、交互事件(onClick/copyOption)、自适应字号等核心属性及底层规则;
- 实战落地:基于TextApplication工程,实现热搜榜、可复制文本、自定义选择菜单等实战需求,解决文本样式定制、溢出处理、交互绑定等高频开发问题。