【学习目标】
- 理解 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水平顺序排布易嵌套纯水平单方向的简单布局复杂页面需多层嵌套高StackZ轴层叠排布低嵌套多组件同一空间层叠(角标)无法实现平面多元素排布中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不绘制。
- 偏移优先级:
- offset 为像素级偏移,当使用 offset 调整位置的组件作为锚点时,对齐位置为设置 offset 之前的位置;
- 从 API Version 11 开始新增 Bias 对象,建议 API 11 及以后使用 bias 设置额外偏移;
- bias 为比例偏移,取值 0–1;
- 链内元素的 bias 属性全部失效,仅链头的 bias 作为整链的偏移生效。
二、核心属性与配置规则
2.1 标准配置格式
- // 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 辅助属性
属性名类型核心作用适用场景注意事项idstring唯一标识所有需要被引用的组件/辅助线/屏障必须唯一,优先级:组件 > guideline > barrieroffset像素偏移精准位置校准、固定间距调整作为锚点时,取调整前的原始位置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)
核心作用:虚拟参考线,批量组件统一对齐到特定偏移位置,避免重复设置偏移。
配置方式:- 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)
核心作用:基于一组组件的最远边界生成动态锚点,避免组件重叠,适配动态内容。
配置方式:- 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 能力演示首页
- 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)
- @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)
- @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)
- @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)
- @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)
- @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工程,实现热搜榜、可复制文本、自定义选择菜单等实战需求,解决文本样式定制、溢出处理、交互绑定等高频开发问题。
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |