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