目錄
概述
基本概念
設置依賴關系
設置參考邊界
設置錨點
設置相對于錨點的對齊位置
子組件位置偏移
多種組件的對齊布局
組件尺寸
多個組件形成鏈
概述
RelativeContainer是一種采用相對布局的容器,支持容器內部的子元素設置相對位置關系,適用于處理界面復雜的場景,對多個子元素進行對齊和排列。子元素可以指定兄弟元素或父容器作為錨點,基于錨點進行相對位置布局。在使用錨點時,需注意子元素的相對位置關系,以避免出現錯位或遮擋的情況。下圖展示了一個 RelativeContainer的概念圖,圖中的虛線表示位置的依賴關系。
圖1?相對布局示意圖
子元素并不完全是上圖中的依賴關系。比如,Item4可以以Item2為依賴錨點,也可以以RelativeContainer父容器為依賴錨點。
基本概念
- 參考邊界:設置當前組件的哪個邊界對齊到錨點。
- 錨點:通過錨點設置當前元素基于哪個元素確定位置。
- 對齊方式:通過對齊方式,設置當前元素是基于錨點的上中下對齊,還是基于錨點的左中右對齊。
設置依賴關系
設置參考邊界
設置當前組件的哪個邊界對齊到錨點。容器內子組件的參考邊界區分水平方向和垂直方向。
-
在水平方向上,可以按照起始(left)、居中(middle)或尾端(right)的組件邊界與錨點對齊。當設置三個邊界時,僅起始(left)和居中(middle)的邊界設置生效。
-
在垂直方向上,可以設置組件邊界與錨點對齊,具體包括頂部(top)、居中(center)和底部(bottom)。當設置三個邊界時,僅頂部(top)和居中(center)生效。
設置錨點
錨點設置涉及子元素相對于其父元素或兄弟元素的位置依賴關系。具體而言,子元素可以將其位置錨定到相對布局容器(RelativeContainer)、輔助線(guideline)、屏障(barrier)或其他子元素上。
為了準確定義錨點,RelativeContainer的子元素必須擁有唯一的組件標識(id),用于指定錨點信息。父元素RelativeContainer的標識默認為“__container__”,其他子元素的組件標識(id)則通過id屬性設置。
- 未設置組件標識(id)的組件雖可顯示,但無法被其他組件引用為錨點。相對布局容器會為其拼接組件標識,但組件標識(id)的規律無法被應用感知。輔助線(guideline)與屏障(barrier)的組件標識(id)需確保唯一,避免與任何組件沖突。若有重復,遵循組件 > guideline > barrier 的優先級。
- 組件間設置錨點時應避免形成依賴循環(組件之間設置鏈除外),依賴循環將導致子組件缺乏定位基準,最終無法繪制。
- RelativeContainer父組件為錨點,__container__代表父容器的組件標識(id)。
let AlignRus: Record<string, Record<string, string | VerticalAlign | HorizontalAlign>> = {'top': { 'anchor': '__container__', 'align': VerticalAlign.Top },'left': { 'anchor': '__container__', 'align': HorizontalAlign.Start }
}
let AlignRue: Record<string, Record<string, string | VerticalAlign | HorizontalAlign>> = {'top': { 'anchor': '__container__', 'align': VerticalAlign.Top },'right': { 'anchor': '__container__', 'align': HorizontalAlign.End }
}
let Mleft: Record<string, number> = { 'left': 20 }
let BWC: Record<string, number | string> = { 'width': 2, 'color': '#6699FF' }@Entry
@Component
struct Index {build() {RelativeContainer() {Row() {Text('row1')}.justifyContent(FlexAlign.Center).width(100).height(100).backgroundColor('#a3cf62').alignRules(AlignRus).id("row1")Row() {Text('row2')}.justifyContent(FlexAlign.Center).width(100).height(100).backgroundColor('#00ae9d').alignRules(AlignRue).id("row2")}.width(300).height(300).margin(Mleft).border(BWC)}
}
- 以兄弟元素為錨點。
-
let AlignRus: Record<string, Record<string, string | VerticalAlign | HorizontalAlign>> = {'top': { 'anchor': '__container__', 'align': VerticalAlign.Top },'left': { 'anchor': '__container__', 'align': HorizontalAlign.Start } } let RelConB: Record<string, Record<string, string | VerticalAlign | HorizontalAlign>> = {'top': { 'anchor': 'row1', 'align': VerticalAlign.Bottom },'left': { 'anchor': 'row1', 'align': HorizontalAlign.Start } } let Mleft: Record<string, number> = { 'left': 20 } let BWC: Record<string, number | string> = { 'width': 2, 'color': '#6699FF' }@Entry @Component struct Index {build() {RelativeContainer() {Row() {Text('row1')}.justifyContent(FlexAlign.Center).width(100).height(100).backgroundColor('#00ae9d').alignRules(AlignRus).id("row1")Row() {Text('row2')}.justifyContent(FlexAlign.Center).width(100).height(100).backgroundColor('#a3cf62').alignRules(RelConB).id("row2")}.width(300).height(300).margin(Mleft).border(BWC)} }
- 子組件錨點可以任意選擇,但需注意不要相互依賴。?
-
@Entry @Component struct Index {build() {Row() {RelativeContainer() {Row(){Text('row1')}.justifyContent(FlexAlign.Center).width(100).height(100).backgroundColor('#a3cf62').alignRules({top: {anchor: "__container__", align: VerticalAlign.Top},left: {anchor: "__container__", align: HorizontalAlign.Start}}).id("row1")Row(){Text('row2')}.justifyContent(FlexAlign.Center).width(100).backgroundColor('#00ae9d').alignRules({top: {anchor: "__container__", align: VerticalAlign.Top},right: {anchor: "__container__", align: HorizontalAlign.End},bottom: {anchor: "row1", align: VerticalAlign.Center},}).id("row2")Row(){Text('row3')}.justifyContent(FlexAlign.Center).height(100).backgroundColor('#0a59f7').alignRules({top: {anchor: "row1", align: VerticalAlign.Bottom},left: {anchor: "row1", align: HorizontalAlign.Start},right: {anchor: "row2", align: HorizontalAlign.Start}}).id("row3")Row(){Text('row4')}.justifyContent(FlexAlign.Center).backgroundColor('#2ca9e0').alignRules({top: {anchor: "row3", align: VerticalAlign.Bottom},left: {anchor: "row1", align: HorizontalAlign.Center},right: {anchor: "row2", align: HorizontalAlign.End},bottom: {anchor: "__container__", align: VerticalAlign.Bottom}}).id("row4")}.width(300).height(300).margin({left: 50}).border({width:2, color: "#6699FF"})}.height('100%')} }
設置相對于錨點的對齊位置
設置了錨點之后,可以通過alignRules屬性的align設置相對于錨點的對齊位置。
在水平方向上,對齊位置可以設置為HorizontalAlign.Start、HorizontalAlign.Center、HorizontalAlign.End。
在豎直方向上,對齊位置可以設置為VerticalAlign.Top、VerticalAlign.Center、VerticalAlign.Bottom。
子組件位置偏移
子組件經過相對位置對齊后,可能尚未達到目標位置。開發者可根據需要設置額外偏移(offset)。當使用offset調整位置的組件作為錨點時,對齊位置為設置offset之前的位置。從API Version 11開始,新增了bias對象,建議API Version 11及以后的版本使用bias來設置額外偏移。
@Entry
@Component
struct Index {
build() {Row() {RelativeContainer() {Row() {Text('row1')}.justifyContent(FlexAlign.Center).width(100).height(100).backgroundColor('#a3cf62').alignRules({top: { anchor: "__container__", align: VerticalAlign.Top },left: { anchor: "__container__", align: HorizontalAlign.Start }}).id("row1")Row() {Text('row2')}.justifyContent(FlexAlign.Center).width(100).backgroundColor('#00ae9d').alignRules({top: { anchor: "__container__", align: VerticalAlign.Top },right: { anchor: "__container__", align: HorizontalAlign.End },bottom: { anchor: "row1", align: VerticalAlign.Center },}).offset({x: -40,y: -20}).id("row2")Row() {Text('row3')}.justifyContent(FlexAlign.Center).height(100).backgroundColor('#0a59f7').alignRules({top: { anchor: "row1", align: VerticalAlign.Bottom },left: { anchor: "row1", align: HorizontalAlign.End },right: { anchor: "row2", align: HorizontalAlign.Start }}).offset({x: -10,y: -20}).id("row3")Row() {Text('row4')}.justifyContent(FlexAlign.Center).backgroundColor('#2ca9e0').alignRules({top: { anchor: "row3", align: VerticalAlign.Bottom },bottom: { anchor: "__container__", align: VerticalAlign.Bottom },left: { anchor: "__container__", align: HorizontalAlign.Start },right: { anchor: "row1", align: HorizontalAlign.End }}).offset({x: -10,y: -30}).id("row4")Row() {Text('row5')}.justifyContent(FlexAlign.Center).backgroundColor('#30c9f7').alignRules({top: { anchor: "row3", align: VerticalAlign.Bottom },bottom: { anchor: "__container__", align: VerticalAlign.Bottom },left: { anchor: "row2", align: HorizontalAlign.Start },right: { anchor: "row2", align: HorizontalAlign.End }}).offset({x: 10,y: 20}).id("row5")Row() {Text('row6')}.justifyContent(FlexAlign.Center).backgroundColor('#ff33ffb5').alignRules({top: { anchor: "row3", align: VerticalAlign.Bottom },bottom: { anchor: "row4", align: VerticalAlign.Bottom },left: { anchor: "row3", align: HorizontalAlign.Start },right: { anchor: "row3", align: HorizontalAlign.End }}).offset({x: -15,y: 10}).backgroundImagePosition(Alignment.Bottom).backgroundImageSize(ImageSize.Cover).id("row6")}.width(300).height(300).margin({ left: 50 }).border({ width: 2, color: "#6699FF" })}.height('100%')
}
}
多種組件的對齊布局
Row、Column、Flex、Stack等多種布局組件,可按照RelativeContainer組件規則進行對齊排布。
@Entry
@Component
struct Index {
@State value: number = 0build() {Row() {RelativeContainer() {Row().width(100).height(100).backgroundColor('#a3cf62').alignRules({top: { anchor: "__container__", align: VerticalAlign.Top },left: { anchor: "__container__", align: HorizontalAlign.Start }}).id("row1")Column().width('50%').height(30).backgroundColor('#00ae9d').alignRules({top: { anchor: "__container__", align: VerticalAlign.Top },left: { anchor: "__container__", align: HorizontalAlign.Center }}).id("row2")Flex({ direction: FlexDirection.Row }) {Text('1').width('20%').height(50).backgroundColor('#0a59f7')Text('2').width('20%').height(50).backgroundColor('#2ca9e0')Text('3').width('20%').height(50).backgroundColor('#0a59f7')Text('4').width('20%').height(50).backgroundColor('#2ca9e0')}.padding(10).backgroundColor('#30c9f7').alignRules({top: { anchor: "row2", align: VerticalAlign.Bottom },left: { anchor: "__container__", align: HorizontalAlign.Start },bottom: { anchor: "__container__", align: VerticalAlign.Center },right: { anchor: "row2", align: HorizontalAlign.Center }}).id("row3")Stack({ alignContent: Alignment.Bottom }) {Text('First child, show in bottom').width('90%').height('100%').backgroundColor('#a3cf62').align(Alignment.Top)Text('Second child, show in top').width('70%').height('60%').backgroundColor('#00ae9d').align(Alignment.Top)}.margin({ top: 5 }).alignRules({top: { anchor: "row3", align: VerticalAlign.Bottom },left: { anchor: "__container__", align: HorizontalAlign.Start },bottom: { anchor: "__container__", align: VerticalAlign.Bottom },right: { anchor: "row3", align: HorizontalAlign.End }}).id("row4")}.width(300).height(300).margin({ left: 50 }).border({ width: 2, color: "#6699FF" })}.height('100%')
}
}
組件尺寸
當同時存在前端頁面設置的子組件尺寸和相對布局規則時,子組件的繪制尺寸依據約束規則確定。從API Version 11開始,此規則有所變化,子組件自身設置的尺寸優先級高于相對布局規則中的對齊錨點尺寸。因此,若要使子組件與錨點嚴格對齊,應僅使用alignRules,避免使用尺寸設置。
說明
- 根據約束條件和子組件自身的size屬性無法確定子組件的大小,此時,不繪制該子組件。
- 在同一方向上設置兩個或更多錨點時,若這些錨點的位置順序有誤,該子組件將被視為大小為0而不予繪制。
@Entry
@Component
struct Index {build() {Row() {RelativeContainer() {Row() {Text('row1')}.justifyContent(FlexAlign.Center).width(100).height(100).backgroundColor('#a3cf62').alignRules({top: { anchor: "__container__", align: VerticalAlign.Top },left: { anchor: "__container__", align: HorizontalAlign.Start }}).id("row1")Row() {Text('row2')}.justifyContent(FlexAlign.Center).width(100).backgroundColor('#00ae9d').alignRules({top: { anchor: "__container__", align: VerticalAlign.Top },right: { anchor: "__container__", align: HorizontalAlign.End },bottom: { anchor: "row1", align: VerticalAlign.Center },}).id("row2")Row() {Text('row3')}.justifyContent(FlexAlign.Center).height(100).backgroundColor('#0a59f7').alignRules({top: { anchor: "row1", align: VerticalAlign.Bottom },left: { anchor: "row1", align: HorizontalAlign.End },right: { anchor: "row2", align: HorizontalAlign.Start }}).id("row3")Row() {Text('row4')}.justifyContent(FlexAlign.Center).backgroundColor('#2ca9e0').alignRules({top: { anchor: "row3", align: VerticalAlign.Bottom },bottom: { anchor: "__container__", align: VerticalAlign.Bottom },left: { anchor: "__container__", align: HorizontalAlign.Start },right: { anchor: "row1", align: HorizontalAlign.End }}).id("row4")Row() {Text('row5')}.justifyContent(FlexAlign.Center).backgroundColor('#30c9f7').alignRules({top: { anchor: "row3", align: VerticalAlign.Bottom },bottom: { anchor: "__container__", align: VerticalAlign.Bottom },left: { anchor: "row2", align: HorizontalAlign.Start },right: { anchor: "row2", align: HorizontalAlign.End }}).id("row5")Row() {Text('row6')}.justifyContent(FlexAlign.Center).backgroundColor('#ff33ffb5').alignRules({top: { anchor: "row3", align: VerticalAlign.Bottom },bottom: { anchor: "row4", align: VerticalAlign.Bottom },left: { anchor: "row3", align: HorizontalAlign.Start },right: { anchor: "row3", align: HorizontalAlign.End }}).id("row6").backgroundImagePosition(Alignment.Bottom).backgroundImageSize(ImageSize.Cover)}.width(300).height(300).margin({ left: 50 }).border({ width: 2, color: "#6699FF" })}.height('100%')}
}
多個組件形成鏈
鏈的形成依賴于組件之間的關聯關系。以組件A和組件B構成的最簡水平鏈為例,其依賴關系為:錨點1 <-- 組件A <---> 組件B --> 錨點2,即A具有left錨點,B具有right錨點,同時A的right錨點與B的HorizontalAlign.Start對齊,B的left錨點與A的HorizontalAlign.End對齊。
- 鏈的方向和格式在鏈頭組件的chainMode接口中聲明;鏈內元素的bias屬性全部失效,鏈頭元素的bias屬性作為整個鏈的bias生效。鏈頭是指在滿足成鏈規則時鏈的第一個組件(在水平方向上,從左邊開始,鏡像語言中從右邊開始;在豎直方向上,從上邊開始)。
- 如果鏈內所有元素的size超出鏈的錨點約束,超出部分將被均勻分配到鏈的兩側。在Packed鏈中,可以通過bias設置超出部分的分布。
@Entry
@Component
struct Index {build() {Row() {RelativeContainer() {Row() {Text('row1')}.justifyContent(FlexAlign.Center).width(80).height(80).backgroundColor('#a3cf62').alignRules({left: { anchor: "__container__", align: HorizontalAlign.Start },right: { anchor: "row2", align: HorizontalAlign.Start },top: { anchor: "__container__", align: VerticalAlign.Top }}).id("row1").chainMode(Axis.Horizontal, ChainStyle.SPREAD)Row() {Text('row2')}.justifyContent(FlexAlign.Center).width(80).height(80).backgroundColor('#00ae9d').alignRules({left: { anchor: "row1", align: HorizontalAlign.End },right: { anchor: "row3", align: HorizontalAlign.Start },top: { anchor: "row1", align: VerticalAlign.Top }}).id("row2")Row() {Text('row3')}.justifyContent(FlexAlign.Center).width(80).height(80).backgroundColor('#0a59f7').alignRules({left: { anchor: "row2", align: HorizontalAlign.End },right: { anchor: "__container__", align: HorizontalAlign.End },top: { anchor: "row1", align: VerticalAlign.Top }}).id("row3")Row() {Text('row4')}.justifyContent(FlexAlign.Center).width(80).height(80).backgroundColor('#a3cf62').alignRules({left: { anchor: "__container__", align: HorizontalAlign.Start },right: { anchor: "row5", align: HorizontalAlign.Start },center: { anchor: "__container__", align: VerticalAlign.Center }}).id("row4").chainMode(Axis.Horizontal, ChainStyle.SPREAD_INSIDE)Row() {Text('row5')}.justifyContent(FlexAlign.Center).width(80).height(80).backgroundColor('#00ae9d').alignRules({left: { anchor: "row4", align: HorizontalAlign.End },right: { anchor: "row6", align: HorizontalAlign.Start },top: { anchor: "row4", align: VerticalAlign.Top }}).id("row5")Row() {Text('row6')}.justifyContent(FlexAlign.Center).width(80).height(80).backgroundColor('#0a59f7').alignRules({left: { anchor: "row5", align: HorizontalAlign.End },right: { anchor: "__container__", align: HorizontalAlign.End },top: { anchor: "row4", align: VerticalAlign.Top }}).id("row6")Row() {Text('row7')}.justifyContent(FlexAlign.Center).width(80).height(80).backgroundColor('#a3cf62').alignRules({left: { anchor: "__container__", align: HorizontalAlign.Start },right: { anchor: "row8", align: HorizontalAlign.Start },bottom: { anchor: "__container__", align: VerticalAlign.Bottom }}).id("row7").chainMode(Axis.Horizontal, ChainStyle.PACKED)Row() {Text('row8')}.justifyContent(FlexAlign.Center).width(80).height(80).backgroundColor('#00ae9d').alignRules({left: { anchor: "row7", align: HorizontalAlign.End },right: { anchor: "row9", align: HorizontalAlign.Start },top: { anchor: "row7", align: VerticalAlign.Top }}).id("row8")Row() {Text('row9')}.justifyContent(FlexAlign.Center).width(80).height(80).backgroundColor('#0a59f7').alignRules({left: { anchor: "row8", align: HorizontalAlign.End },right: { anchor: "__container__", align: HorizontalAlign.End },top: { anchor: "row7", align: VerticalAlign.Top }}).id("row9")}.width(300).height(300).margin({ left: 50 }).border({ width: 2, color: "#6699FF" })}.height('100%')}
}