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![relative-layout](figures/relative-layout.png)
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  ![zh-cn_image_0000001562820901](figures/zh-cn_image_0000001562820901.png)
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  ![zh-cn_image_0000001562940613](figures/zh-cn_image_0000001562940613.png)
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  ![Simplify-Component-Layout](figures/arkts-simplify-component-layout-image1.png)
186
187### 设置相对于锚点的对齐位置
188
189设置了锚点之后,可以通过align设置相对于锚点的对齐位置。
190
191在水平方向上,对齐位置可以设置为HorizontalAlign.StartHorizontalAlign.CenterHorizontalAlign.End192
193![alignment-relative-anchor-horizontal](figures/alignment-relative-anchor-horizontal.png)
194
195在竖直方向上,对齐位置可以设置为VerticalAlign.TopVerticalAlign.CenterVerticalAlign.Bottom196
197![alignment-relative-anchor-vertical](figures/alignment-relative-anchor-vertical.png)
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  ![Simplify-Component-Layout](figures/arkts-simplify-component-layout-image2.png)
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  ![Simplify-Component-Layout](figures/arkts-simplify-component-layout-image3.png)
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  ![Simplify-Component-Layout](figures/arkts-simplify-component-layout-image4.png)
492