1# 相对布局 (RelativeContainer) 2 3 4## 概述 5 6在应用的开发过程中,经常需要设计复杂界面,此时涉及到多个相同或不同组件之间的嵌套。如果布局组件嵌套深度过深,或者嵌套组件数过多,会带来额外的开销。如果在布局的方式上进行优化,就可以有效的提升性能,减少时间开销。<!--Del-->请参考[优化布局时间](../performance/reduce-view-nesting-levels.md#优化布局时间)了解RelativeContainer相对于List,在布局时间上的性能提升。<!--DelEnd--> 7 8RelativeContainer为采用相对布局的容器,支持容器内部的子元素设置相对位置关系,适用于界面复杂场景的情况,对多个子组件进行对齐和排列。子元素支持指定兄弟元素作为锚点,也支持指定父容器作为锚点,基于锚点做相对位置布局。下图是一个RelativeContainer的概念图,图中的虚线表示位置的依赖关系。 9 10 11 **图1** 相对布局示意图 12 13 14 15 16子元素并不完全是上图中的依赖关系。比如,Item4可以以Item2为依赖锚点,也可以以RelativeContainer父容器为依赖锚点。 17 18 19## 基本概念 20 21- 锚点:通过锚点设置当前元素基于哪个元素确定位置。 22 23- 对齐方式:通过对齐方式,设置当前元素是基于锚点的上中下对齐,还是基于锚点的左中右对齐。 24 25 26## 设置依赖关系 27 28 29### 锚点设置 30 31锚点设置是指设置子元素相对于父元素或兄弟元素的位置依赖关系。在水平方向上,可以设置left、middle、right的锚点。在竖直方向上,可以设置top、center、bottom的锚点。 32为了明确定义锚点,必须为RelativeContainer及其子元素设置ID,用于指定锚点信息。ID默认为“\_\_container\_\_”,其余子元素的ID通过id属性设置。不设置id的组件能显示,但是不能被其他子组件作为锚点,相对布局容器会为其拼接id,此id的规律无法被应用感知。互相依赖,环形依赖时容器内子组件全部不绘制。同方向上两个以上位置设置锚点,但锚点位置逆序时此子组件大小为0,即不绘制。 33 34>**说明:** 35> 36>在使用锚点时要注意子元素的相对位置关系,避免出现错位或遮挡的情况。 37 38- RelativeContainer父组件为锚点,__container__代表父容器的ID。 39 40 ```ts 41 let AlignRus:Record<string,Record<string,string|VerticalAlign|HorizontalAlign>> = { 42 'top': { 'anchor': '__container__', 'align': VerticalAlign.Top }, 43 'left': { 'anchor': '__container__', 'align': HorizontalAlign.Start } 44 } 45 let AlignRue:Record<string,Record<string,string|VerticalAlign|HorizontalAlign>> = { 46 'top': { 'anchor': '__container__', 'align': VerticalAlign.Top }, 47 'right': { 'anchor': '__container__', 'align': HorizontalAlign.End } 48 } 49 let Mleft:Record<string,number> = { 'left': 20 } 50 let BWC:Record<string,number|string> = { 'width': 2, 'color': '#6699FF' } 51 52 @Entry 53 @Component 54 struct Index { 55 build() { 56 RelativeContainer() { 57 Row(){Text('row1')}.justifyContent(FlexAlign.Center).width(100).height(100) 58 .backgroundColor("#FF3333") 59 .alignRules(AlignRus) 60 .id("row1") 61 62 Row(){Text('row2')}.justifyContent(FlexAlign.Center).width(100).height(100) 63 .backgroundColor("#FFCC00") 64 .alignRules(AlignRue) 65 .id("row2") 66 }.width(300).height(300) 67 .margin(Mleft) 68 .border(BWC) 69 } 70 } 71 ``` 72 73  74 75- 以兄弟元素为锚点。 76 77 ```ts 78 let AlignRus:Record<string,Record<string,string|VerticalAlign|HorizontalAlign>> = { 79 'top': { 'anchor': '__container__', 'align': VerticalAlign.Top }, 80 'left': { 'anchor': '__container__', 'align': HorizontalAlign.Start } 81 } 82 let RelConB:Record<string,Record<string,string|VerticalAlign|HorizontalAlign>> = { 83 'top': { 'anchor': 'row1', 'align': VerticalAlign.Bottom }, 84 'left' : { 'anchor': 'row1', 'align': HorizontalAlign.Start } 85 } 86 let Mleft:Record<string,number> = { 'left': 20 } 87 let BWC:Record<string,number|string> = { 'width': 2, 'color': '#6699FF' } 88 89 @Entry 90 @Component 91 struct Index { 92 build() { 93 RelativeContainer() { 94 Row(){Text('row1')}.justifyContent(FlexAlign.Center).width(100).height(100) 95 .backgroundColor("#FF3333") 96 .alignRules(AlignRus) 97 .id("row1") 98 99 Row(){Text('row2')}.justifyContent(FlexAlign.Center).width(100).height(100) 100 .backgroundColor("#FFCC00") 101 .alignRules(RelConB) 102 .id("row2") 103 }.width(300).height(300) 104 .margin(Mleft) 105 .border(BWC) 106 } 107 } 108 ``` 109 110  111 112- 子组件锚点可以任意选择,但需注意不要相互依赖。 113 114 ```ts 115 @Entry 116 @Component 117 struct Index { 118 build() { 119 Row() { 120 RelativeContainer() { 121 Row(){Text('row1')}.justifyContent(FlexAlign.Center).width(100).height(100) 122 .backgroundColor('#ff3339ff') 123 .alignRules({ 124 top: {anchor: "__container__", align: VerticalAlign.Top}, 125 left: {anchor: "__container__", align: HorizontalAlign.Start} 126 }) 127 .id("row1") 128 129 Row(){Text('row2')}.justifyContent(FlexAlign.Center).width(100) 130 .backgroundColor('#ff298e1e') 131 .alignRules({ 132 top: {anchor: "__container__", align: VerticalAlign.Top}, 133 right: {anchor: "__container__", align: HorizontalAlign.End}, 134 bottom: {anchor: "row1", align: VerticalAlign.Center}, 135 }) 136 .id("row2") 137 138 Row(){Text('row3')}.justifyContent(FlexAlign.Center).height(100) 139 .backgroundColor('#ffff6a33') 140 .alignRules({ 141 top: {anchor: "row1", align: VerticalAlign.Bottom}, 142 left: {anchor: "row1", align: HorizontalAlign.Start}, 143 right: {anchor: "row2", align: HorizontalAlign.Start} 144 }) 145 .id("row3") 146 147 Row(){Text('row4')}.justifyContent(FlexAlign.Center) 148 .backgroundColor('#ffff33fd') 149 .alignRules({ 150 top: {anchor: "row3", align: VerticalAlign.Bottom}, 151 left: {anchor: "row1", align: HorizontalAlign.Center}, 152 right: {anchor: "row2", align: HorizontalAlign.End}, 153 bottom: {anchor: "__container__", align: VerticalAlign.Bottom} 154 }) 155 .id("row4") 156 } 157 .width(300).height(300) 158 .margin({left: 50}) 159 .border({width:2, color: "#6699FF"}) 160 } 161 .height('100%') 162 } 163 } 164 ``` 165  166 167### 设置相对于锚点的对齐位置 168 169设置了锚点之后,可以通过align设置相对于锚点的对齐位置。 170 171在水平方向上,对齐位置可以设置为HorizontalAlign.Start、HorizontalAlign.Center、HorizontalAlign.End。 172 173 174 175在竖直方向上,对齐位置可以设置为VerticalAlign.Top、VerticalAlign.Center、VerticalAlign.Bottom。 176 177 178 179### 子组件位置偏移 180 181子组件经过相对位置对齐后,位置可能还不是目标位置,开发者可根据需要进行额外偏移设置offset。 182 183 ```ts 184 @Entry 185 @Component 186 struct Index { 187 build() { 188 Row() { 189 RelativeContainer() { 190 Row(){Text('row1')}.justifyContent(FlexAlign.Center).width(100).height(100) 191 .backgroundColor("#FF3333") 192 .alignRules({ 193 top: {anchor: "__container__", align: VerticalAlign.Top}, 194 left: {anchor: "__container__", align: HorizontalAlign.Start} 195 }) 196 .id("row1") 197 198 Row(){Text('row2')}.justifyContent(FlexAlign.Center).width(100) 199 .backgroundColor("#FFCC00") 200 .alignRules({ 201 top: {anchor: "__container__", align: VerticalAlign.Top}, 202 right: {anchor: "__container__", align: HorizontalAlign.End}, 203 bottom: {anchor: "row1", align: VerticalAlign.Center}, 204 }) 205 .offset({ 206 x:-40, 207 y:-20 208 }) 209 .id("row2") 210 211 Row(){Text('row3')}.justifyContent(FlexAlign.Center).height(100) 212 .backgroundColor("#FF6633") 213 .alignRules({ 214 top: {anchor: "row1", align: VerticalAlign.Bottom}, 215 left: {anchor: "row1", align: HorizontalAlign.End}, 216 right: {anchor: "row2", align: HorizontalAlign.Start} 217 }) 218 .offset({ 219 x:-10, 220 y:-20 221 }) 222 .id("row3") 223 224 Row(){Text('row4')}.justifyContent(FlexAlign.Center) 225 .backgroundColor("#FF9966") 226 .alignRules({ 227 top: {anchor: "row3", align: VerticalAlign.Bottom}, 228 bottom: {anchor: "__container__", align: VerticalAlign.Bottom}, 229 left: {anchor: "__container__", align: HorizontalAlign.Start}, 230 right: {anchor: "row1", align: HorizontalAlign.End} 231 }) 232 .offset({ 233 x:-10, 234 y:-30 235 }) 236 .id("row4") 237 238 Row(){Text('row5')}.justifyContent(FlexAlign.Center) 239 .backgroundColor("#FF66FF") 240 .alignRules({ 241 top: {anchor: "row3", align: VerticalAlign.Bottom}, 242 bottom: {anchor: "__container__", align: VerticalAlign.Bottom}, 243 left: {anchor: "row2", align: HorizontalAlign.Start}, 244 right: {anchor: "row2", align: HorizontalAlign.End} 245 }) 246 .offset({ 247 x:10, 248 y:20 249 }) 250 .id("row5") 251 252 Row(){Text('row6')}.justifyContent(FlexAlign.Center) 253 .backgroundColor('#ff33ffb5') 254 .alignRules({ 255 top: {anchor: "row3", align: VerticalAlign.Bottom}, 256 bottom: {anchor: "row4", align: VerticalAlign.Bottom}, 257 left: {anchor: "row3", align: HorizontalAlign.Start}, 258 right: {anchor: "row3", align: HorizontalAlign.End} 259 }) 260 .offset({ 261 x:-15, 262 y:10 263 }) 264 .backgroundImagePosition(Alignment.Bottom) 265 .backgroundImageSize(ImageSize.Cover) 266 .id("row6") 267 } 268 .width(300).height(300) 269 .margin({left: 50}) 270 .border({width:2, color: "#6699FF"}) 271 } 272 .height('100%') 273 } 274 } 275 ``` 276  277 278## 多种组件的对齐布局 279 280Row、Column、Flex、Stack等多种布局组件,可按照RelativeContainer组件规则进行对其排布。 281 282 ```ts 283 @Entry 284 @Component 285 struct Index { 286 @State value: number = 0 287 build() { 288 Row() { 289 290 RelativeContainer() { 291 Row().width(100).height(100) 292 .backgroundColor('#ff33ffcc') 293 .alignRules({ 294 top: {anchor: "__container__", align: VerticalAlign.Top}, 295 left: {anchor: "__container__", align: HorizontalAlign.Start} 296 }) 297 .id("row1") 298 299 Column().width('50%').height(30).backgroundColor(0xAFEEEE) 300 .alignRules({ 301 top: {anchor: "__container__", align: VerticalAlign.Top}, 302 left: {anchor: "__container__", align: HorizontalAlign.Center} 303 }).id("row2") 304 305 Flex({ direction: FlexDirection.Row }) { 306 Text('1').width('20%').height(50).backgroundColor(0xF5DEB3) 307 Text('2').width('20%').height(50).backgroundColor(0xD2B48C) 308 Text('3').width('20%').height(50).backgroundColor(0xF5DEB3) 309 Text('4').width('20%').height(50).backgroundColor(0xD2B48C) 310 } 311 .padding(10) 312 .backgroundColor('#ffedafaf') 313 .alignRules({ 314 top: {anchor: "row2", align: VerticalAlign.Bottom}, 315 left: {anchor: "__container__", align: HorizontalAlign.Start}, 316 bottom: {anchor: "__container__", align: VerticalAlign.Center}, 317 right: {anchor: "row2", align: HorizontalAlign.Center} 318 }) 319 .id("row3") 320 321 Stack({ alignContent: Alignment.Bottom }) { 322 Text('First child, show in bottom').width('90%').height('100%').backgroundColor(0xd2cab3).align(Alignment.Top) 323 Text('Second child, show in top').width('70%').height('60%').backgroundColor(0xc1cbac).align(Alignment.Top) 324 } 325 .margin({ top: 5 }) 326 .alignRules({ 327 top: {anchor: "row3", align: VerticalAlign.Bottom}, 328 left: {anchor: "__container__", align: HorizontalAlign.Start}, 329 bottom: {anchor: "__container__", align: VerticalAlign.Bottom}, 330 right: {anchor: "row3", align: HorizontalAlign.End} 331 }) 332 .id("row4") 333 334 } 335 .width(300).height(300) 336 .margin({left: 50}) 337 .border({width:2, color: "#6699FF"}) 338 } 339 .height('100%') 340 } 341 } 342 ``` 343  344 345## 组件尺寸 346 347子组件尺寸大小不会受到相对布局规则的影响。若子组件某个方向上设置两个或以上alignRules时最好不设置此方向尺寸大小,否则对齐规则确定的组件尺寸与开发者设置的尺寸可能产生冲突。 348 349 ```ts 350 @Entry 351 @Component 352 struct Index { 353 build() { 354 Row() { 355 RelativeContainer() { 356 Row(){Text('row1')}.justifyContent(FlexAlign.Center) 357 .width(100).height(100) 358 .backgroundColor("#FF3333") 359 .alignRules({ 360 top: {anchor: "__container__", align: VerticalAlign.Top}, 361 left: {anchor: "__container__", align: HorizontalAlign.Start} 362 }) 363 .id("row1") 364 365 Row(){Text('row2')}.justifyContent(FlexAlign.Center).width(100) 366 .backgroundColor("#FFCC00") 367 .alignRules({ 368 top: {anchor: "__container__", align: VerticalAlign.Top}, 369 right: {anchor: "__container__", align: HorizontalAlign.End}, 370 bottom: {anchor: "row1", align: VerticalAlign.Center}, 371 }) 372 .id("row2") 373 374 Row(){Text('row3')}.justifyContent(FlexAlign.Center).height(100) 375 .backgroundColor("#FF6633") 376 .alignRules({ 377 top: {anchor: "row1", align: VerticalAlign.Bottom}, 378 left: {anchor: "row1", align: HorizontalAlign.End}, 379 right: {anchor: "row2", align: HorizontalAlign.Start} 380 }) 381 .id("row3") 382 383 Row(){Text('row4')}.justifyContent(FlexAlign.Center) 384 .backgroundColor("#FF9966") 385 .alignRules({ 386 top: {anchor: "row3", align: VerticalAlign.Bottom}, 387 bottom: {anchor: "__container__", align: VerticalAlign.Bottom}, 388 left: {anchor: "__container__", align: HorizontalAlign.Start}, 389 right: {anchor: "row1", align: HorizontalAlign.End} 390 }) 391 .id("row4") 392 393 Row(){Text('row5')}.justifyContent(FlexAlign.Center) 394 .backgroundColor("#FF66FF") 395 .alignRules({ 396 top: {anchor: "row3", align: VerticalAlign.Bottom}, 397 bottom: {anchor: "__container__", align: VerticalAlign.Bottom}, 398 left: {anchor: "row2", align: HorizontalAlign.Start}, 399 right: {anchor: "row2", align: HorizontalAlign.End} 400 }) 401 .id("row5") 402 403 Row(){Text('row6')}.justifyContent(FlexAlign.Center) 404 .backgroundColor('#ff33ffb5') 405 .alignRules({ 406 top: {anchor: "row3", align: VerticalAlign.Bottom}, 407 bottom: {anchor: "row4", align: VerticalAlign.Bottom}, 408 left: {anchor: "row3", align: HorizontalAlign.Start}, 409 right: {anchor: "row3", align: HorizontalAlign.End} 410 }) 411 .id("row6") 412 .backgroundImagePosition(Alignment.Bottom) 413 .backgroundImageSize(ImageSize.Cover) 414 } 415 .width(300).height(300) 416 .margin({left: 50}) 417 .border({width:2, color: "#6699FF"}) 418 } 419 .height('100%') 420 } 421 } 422 ``` 423  424