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() { 58 Text('row1') 59 } 60 .justifyContent(FlexAlign.Center) 61 .width(100) 62 .height(100) 63 .backgroundColor('#a3cf62') 64 .alignRules(AlignRus) 65 .id("row1") 66 67 Row() { 68 Text('row2') 69 } 70 .justifyContent(FlexAlign.Center) 71 .width(100) 72 .height(100) 73 .backgroundColor('#00ae9d') 74 .alignRules(AlignRue) 75 .id("row2") 76 }.width(300).height(300) 77 .margin(Mleft) 78 .border(BWC) 79 } 80 } 81 ``` 82 83  84 85- 以兄弟元素为锚点。 86 87 ```ts 88 let AlignRus: Record<string, Record<string, string | VerticalAlign | HorizontalAlign>> = { 89 'top': { 'anchor': '__container__', 'align': VerticalAlign.Top }, 90 'left': { 'anchor': '__container__', 'align': HorizontalAlign.Start } 91 } 92 let RelConB: Record<string, Record<string, string | VerticalAlign | HorizontalAlign>> = { 93 'top': { 'anchor': 'row1', 'align': VerticalAlign.Bottom }, 94 'left': { 'anchor': 'row1', 'align': HorizontalAlign.Start } 95 } 96 let Mleft: Record<string, number> = { 'left': 20 } 97 let BWC: Record<string, number | string> = { 'width': 2, 'color': '#6699FF' } 98 99 @Entry 100 @Component 101 struct Index { 102 build() { 103 RelativeContainer() { 104 Row() { 105 Text('row1') 106 } 107 .justifyContent(FlexAlign.Center) 108 .width(100) 109 .height(100) 110 .backgroundColor('#00ae9d') 111 .alignRules(AlignRus) 112 .id("row1") 113 114 Row() { 115 Text('row2') 116 } 117 .justifyContent(FlexAlign.Center) 118 .width(100) 119 .height(100) 120 .backgroundColor('#a3cf62') 121 .alignRules(RelConB) 122 .id("row2") 123 }.width(300).height(300) 124 .margin(Mleft) 125 .border(BWC) 126 } 127 } 128 ``` 129 130  131 132- 子组件锚点可以任意选择,但需注意不要相互依赖。 133 134 ```ts 135 @Entry 136 @Component 137 struct Index { 138 build() { 139 Row() { 140 RelativeContainer() { 141 Row(){Text('row1')}.justifyContent(FlexAlign.Center).width(100).height(100) 142 .backgroundColor('#a3cf62') 143 .alignRules({ 144 top: {anchor: "__container__", align: VerticalAlign.Top}, 145 left: {anchor: "__container__", align: HorizontalAlign.Start} 146 }) 147 .id("row1") 148 149 Row(){Text('row2')}.justifyContent(FlexAlign.Center).width(100) 150 .backgroundColor('#00ae9d') 151 .alignRules({ 152 top: {anchor: "__container__", align: VerticalAlign.Top}, 153 right: {anchor: "__container__", align: HorizontalAlign.End}, 154 bottom: {anchor: "row1", align: VerticalAlign.Center}, 155 }) 156 .id("row2") 157 158 Row(){Text('row3')}.justifyContent(FlexAlign.Center).height(100) 159 .backgroundColor('#0a59f7') 160 .alignRules({ 161 top: {anchor: "row1", align: VerticalAlign.Bottom}, 162 left: {anchor: "row1", align: HorizontalAlign.Start}, 163 right: {anchor: "row2", align: HorizontalAlign.Start} 164 }) 165 .id("row3") 166 167 Row(){Text('row4')}.justifyContent(FlexAlign.Center) 168 .backgroundColor('#2ca9e0') 169 .alignRules({ 170 top: {anchor: "row3", align: VerticalAlign.Bottom}, 171 left: {anchor: "row1", align: HorizontalAlign.Center}, 172 right: {anchor: "row2", align: HorizontalAlign.End}, 173 bottom: {anchor: "__container__", align: VerticalAlign.Bottom} 174 }) 175 .id("row4") 176 } 177 .width(300).height(300) 178 .margin({left: 50}) 179 .border({width:2, color: "#6699FF"}) 180 } 181 .height('100%') 182 } 183 } 184 ``` 185  186 187### 设置相对于锚点的对齐位置 188 189设置了锚点之后,可以通过align设置相对于锚点的对齐位置。 190 191在水平方向上,对齐位置可以设置为HorizontalAlign.Start、HorizontalAlign.Center、HorizontalAlign.End。 192 193 194 195在竖直方向上,对齐位置可以设置为VerticalAlign.Top、VerticalAlign.Center、VerticalAlign.Bottom。 196 197 198 199### 子组件位置偏移 200 201子组件经过相对位置对齐后,位置可能还不是目标位置,开发者可根据需要进行额外偏移设置offset。 202 203 ```ts 204@Entry 205@Component 206struct Index { 207 build() { 208 Row() { 209 RelativeContainer() { 210 Row() { 211 Text('row1') 212 } 213 .justifyContent(FlexAlign.Center) 214 .width(100) 215 .height(100) 216 .backgroundColor('#a3cf62') 217 .alignRules({ 218 top: { anchor: "__container__", align: VerticalAlign.Top }, 219 left: { anchor: "__container__", align: HorizontalAlign.Start } 220 }) 221 .id("row1") 222 223 Row() { 224 Text('row2') 225 } 226 .justifyContent(FlexAlign.Center) 227 .width(100) 228 .backgroundColor('#00ae9d') 229 .alignRules({ 230 top: { anchor: "__container__", align: VerticalAlign.Top }, 231 right: { anchor: "__container__", align: HorizontalAlign.End }, 232 bottom: { anchor: "row1", align: VerticalAlign.Center }, 233 }) 234 .offset({ 235 x: -40, 236 y: -20 237 }) 238 .id("row2") 239 240 Row() { 241 Text('row3') 242 } 243 .justifyContent(FlexAlign.Center) 244 .height(100) 245 .backgroundColor('#0a59f7') 246 .alignRules({ 247 top: { anchor: "row1", align: VerticalAlign.Bottom }, 248 left: { anchor: "row1", align: HorizontalAlign.End }, 249 right: { anchor: "row2", align: HorizontalAlign.Start } 250 }) 251 .offset({ 252 x: -10, 253 y: -20 254 }) 255 .id("row3") 256 257 Row() { 258 Text('row4') 259 } 260 .justifyContent(FlexAlign.Center) 261 .backgroundColor('#2ca9e0') 262 .alignRules({ 263 top: { anchor: "row3", align: VerticalAlign.Bottom }, 264 bottom: { anchor: "__container__", align: VerticalAlign.Bottom }, 265 left: { anchor: "__container__", align: HorizontalAlign.Start }, 266 right: { anchor: "row1", align: HorizontalAlign.End } 267 }) 268 .offset({ 269 x: -10, 270 y: -30 271 }) 272 .id("row4") 273 274 Row() { 275 Text('row5') 276 } 277 .justifyContent(FlexAlign.Center) 278 .backgroundColor('#30c9f7') 279 .alignRules({ 280 top: { anchor: "row3", align: VerticalAlign.Bottom }, 281 bottom: { anchor: "__container__", align: VerticalAlign.Bottom }, 282 left: { anchor: "row2", align: HorizontalAlign.Start }, 283 right: { anchor: "row2", align: HorizontalAlign.End } 284 }) 285 .offset({ 286 x: 10, 287 y: 20 288 }) 289 .id("row5") 290 291 Row() { 292 Text('row6') 293 } 294 .justifyContent(FlexAlign.Center) 295 .backgroundColor('#ff33ffb5') 296 .alignRules({ 297 top: { anchor: "row3", align: VerticalAlign.Bottom }, 298 bottom: { anchor: "row4", align: VerticalAlign.Bottom }, 299 left: { anchor: "row3", align: HorizontalAlign.Start }, 300 right: { anchor: "row3", align: HorizontalAlign.End } 301 }) 302 .offset({ 303 x: -15, 304 y: 10 305 }) 306 .backgroundImagePosition(Alignment.Bottom) 307 .backgroundImageSize(ImageSize.Cover) 308 .id("row6") 309 } 310 .width(300).height(300) 311 .margin({ left: 50 }) 312 .border({ width: 2, color: "#6699FF" }) 313 } 314 .height('100%') 315 } 316} 317 ``` 318  319 320## 多种组件的对齐布局 321 322Row、Column、Flex、Stack等多种布局组件,可按照RelativeContainer组件规则进行对齐排布。 323 324 ```ts 325@Entry 326@Component 327struct Index { 328 @State value: number = 0 329 330 build() { 331 Row() { 332 333 RelativeContainer() { 334 Row() 335 .width(100) 336 .height(100) 337 .backgroundColor('#a3cf62') 338 .alignRules({ 339 top: { anchor: "__container__", align: VerticalAlign.Top }, 340 left: { anchor: "__container__", align: HorizontalAlign.Start } 341 }) 342 .id("row1") 343 344 Column() 345 .width('50%') 346 .height(30) 347 .backgroundColor('#00ae9d') 348 .alignRules({ 349 top: { anchor: "__container__", align: VerticalAlign.Top }, 350 left: { anchor: "__container__", align: HorizontalAlign.Center } 351 }) 352 .id("row2") 353 354 Flex({ direction: FlexDirection.Row }) { 355 Text('1').width('20%').height(50).backgroundColor('#0a59f7') 356 Text('2').width('20%').height(50).backgroundColor('#2ca9e0') 357 Text('3').width('20%').height(50).backgroundColor('#0a59f7') 358 Text('4').width('20%').height(50).backgroundColor('#2ca9e0') 359 } 360 .padding(10) 361 .backgroundColor('#30c9f7') 362 .alignRules({ 363 top: { anchor: "row2", align: VerticalAlign.Bottom }, 364 left: { anchor: "__container__", align: HorizontalAlign.Start }, 365 bottom: { anchor: "__container__", align: VerticalAlign.Center }, 366 right: { anchor: "row2", align: HorizontalAlign.Center } 367 }) 368 .id("row3") 369 370 Stack({ alignContent: Alignment.Bottom }) { 371 Text('First child, show in bottom').width('90%').height('100%').backgroundColor('#a3cf62').align(Alignment.Top) 372 Text('Second child, show in top').width('70%').height('60%').backgroundColor('#00ae9d').align(Alignment.Top) 373 } 374 .margin({ top: 5 }) 375 .alignRules({ 376 top: { anchor: "row3", align: VerticalAlign.Bottom }, 377 left: { anchor: "__container__", align: HorizontalAlign.Start }, 378 bottom: { anchor: "__container__", align: VerticalAlign.Bottom }, 379 right: { anchor: "row3", align: HorizontalAlign.End } 380 }) 381 .id("row4") 382 383 } 384 .width(300).height(300) 385 .margin({ left: 50 }) 386 .border({ width: 2, color: "#6699FF" }) 387 } 388 .height('100%') 389 } 390} 391 ``` 392  393 394## 组件尺寸 395 396子组件尺寸大小不会受到相对布局规则的影响。若子组件某个方向上设置两个或以上alignRules时最好不设置此方向尺寸大小,否则对齐规则确定的组件尺寸与开发者设置的尺寸可能产生冲突。 397 398 ```ts 399@Entry 400@Component 401struct Index { 402 build() { 403 Row() { 404 RelativeContainer() { 405 Row() { 406 Text('row1') 407 } 408 .justifyContent(FlexAlign.Center) 409 .width(100) 410 .height(100) 411 .backgroundColor('#a3cf62') 412 .alignRules({ 413 top: { anchor: "__container__", align: VerticalAlign.Top }, 414 left: { anchor: "__container__", align: HorizontalAlign.Start } 415 }) 416 .id("row1") 417 418 Row() { 419 Text('row2') 420 } 421 .justifyContent(FlexAlign.Center) 422 .width(100) 423 .backgroundColor('#00ae9d') 424 .alignRules({ 425 top: { anchor: "__container__", align: VerticalAlign.Top }, 426 right: { anchor: "__container__", align: HorizontalAlign.End }, 427 bottom: { anchor: "row1", align: VerticalAlign.Center }, 428 }) 429 .id("row2") 430 431 Row() { 432 Text('row3') 433 } 434 .justifyContent(FlexAlign.Center) 435 .height(100) 436 .backgroundColor('#0a59f7') 437 .alignRules({ 438 top: { anchor: "row1", align: VerticalAlign.Bottom }, 439 left: { anchor: "row1", align: HorizontalAlign.End }, 440 right: { anchor: "row2", align: HorizontalAlign.Start } 441 }) 442 .id("row3") 443 444 Row() { 445 Text('row4') 446 }.justifyContent(FlexAlign.Center) 447 .backgroundColor('#2ca9e0') 448 .alignRules({ 449 top: { anchor: "row3", align: VerticalAlign.Bottom }, 450 bottom: { anchor: "__container__", align: VerticalAlign.Bottom }, 451 left: { anchor: "__container__", align: HorizontalAlign.Start }, 452 right: { anchor: "row1", align: HorizontalAlign.End } 453 }) 454 .id("row4") 455 456 Row() { 457 Text('row5') 458 }.justifyContent(FlexAlign.Center) 459 .backgroundColor('#30c9f7') 460 .alignRules({ 461 top: { anchor: "row3", align: VerticalAlign.Bottom }, 462 bottom: { anchor: "__container__", align: VerticalAlign.Bottom }, 463 left: { anchor: "row2", align: HorizontalAlign.Start }, 464 right: { anchor: "row2", align: HorizontalAlign.End } 465 }) 466 .id("row5") 467 468 Row() { 469 Text('row6') 470 } 471 .justifyContent(FlexAlign.Center) 472 .backgroundColor('#ff33ffb5') 473 .alignRules({ 474 top: { anchor: "row3", align: VerticalAlign.Bottom }, 475 bottom: { anchor: "row4", align: VerticalAlign.Bottom }, 476 left: { anchor: "row3", align: HorizontalAlign.Start }, 477 right: { anchor: "row3", align: HorizontalAlign.End } 478 }) 479 .id("row6") 480 .backgroundImagePosition(Alignment.Bottom) 481 .backgroundImageSize(ImageSize.Cover) 482 } 483 .width(300).height(300) 484 .margin({ left: 50 }) 485 .border({ width: 2, color: "#6699FF" }) 486 } 487 .height('100%') 488 } 489} 490 ``` 491  492