HarmonyOS Layout Practice 1: Implementation of Common TitleBar
Background
TitleBar is the most commonly used component in daily development. Although each application may have a different style, the overall layout generally consists of three areas:
- Left return area
- Middle title area
- Right button area
Below are screenshots of the TitleBar effects from WeChat and My Huawei applications:
In WeChat, the content area is centered, while in My Huawei, it is left-aligned. In practical development, we may encounter both scenarios. Let's encapsulate a TitleBar to achieve the following functions:
- The far left supports a return button + text.
- The right side can have up to two buttons.
- The middle title area should adaptively occupy space without exceeding the left and right areas, and can be controlled to align left or center.
Selection of Layout Container
The overall effect requires fixed left and right areas with the middle area expanding. In Android, we would first think of RelativeLayout, and HarmonyOS provides a similar relative layout called RelativeContainer. RelativeContainer supports setting relative position relationships for child elements, making it suitable for complex interface scenarios to align and arrange multiple sub-components. Child elements can specify siblings or the parent container as anchors for relative positioning.
RelativeContainer introduces two concepts: anchors and alignment methods:
-
Anchors: Set the position of the current element based on another element. Anchor settings define the positional dependency of child elements relative to the parent or siblings. Horizontally, left, middle, and right anchors can be set; vertically, top, center, and bottom anchors are supported. IDs must be set for the RelativeContainer and its child elements to specify anchor information. The RelativeContainer's default ID is "container", and other child elements use the
id
attribute. Components without an ID can still be displayed but cannot be used as anchors by other children. The framework will generate an ID for them, but the pattern is unpredictable and cannot be directly used. - Alignment Methods: Define whether the current element aligns top/middle/bottom or left/middle/right relative to the anchor.
Notes: Avoid circular dependencies, as they will cause all child components in the container to fail to render. If more than two position anchors are set in the same direction, and the anchor positions are out of order, the child component will have a size of 0 (i.e., not render).
Layout Implementation
The parent layout uses RelativeContainer. The left and right buttons are positioned relative to the parent's left and right sides, while the middle container is positioned relative to the right of the left layout and the left of the right layout. The overall structure is as follows:
build() {
RelativeContainer() {
Text('左侧')
.fontSize(10)
.fontWeight(FontWeight.Bold)
.id("base_title_left_container")
.alignRules({
bottom: { anchor: "__container__", align: VerticalAlign.Bottom },
left: { anchor: "__container__", align: HorizontalAlign.Start }
})
Text('右侧')
.fontSize(10)
.fontWeight(FontWeight.Bold)
.id("base_title_right_container")
.alignRules({
bottom: { anchor: "__container__", align: VerticalAlign.Bottom },
right: { anchor: "__container__", align: HorizontalAlign.End }
})
// .backgroundColor('#00ff00')
Row(){
Text('中间内容')
.fontSize(10)
.fontWeight(FontWeight.Bold)
}.alignRules({
bottom: { anchor: "__container__", align: VerticalAlign.Bottom },
left: { anchor: "base_title_left_container", align: HorizontalAlign.End },
right: { anchor: "base_title_right_container", align: HorizontalAlign.Start }
}).backgroundColor('#ff0000')
}
.height('40')
.width('100%')
.backgroundColor('#ccc')
.margin({top:100})
}
The display effect is as follows:
This meets expectations. When the right side needs to display two buttons, wrap them in a container:
build() {
RelativeContainer() {
Text('左侧')
.fontSize(10)
.fontWeight(FontWeight.Bold)
.id("base_title_left_container")
.alignRules({
bottom: { anchor: "__container__", align: VerticalAlign.Bottom },
left: { anchor: "__container__", align: HorizontalAlign.Start }
})
Flex({ justifyContent: FlexAlign.End, alignItems: ItemAlign.Center }) {
Text('右侧2')
.fontSize(10)
.fontWeight(FontWeight.Bold)
Text('右侧1')
.fontSize(10)
.fontWeight(FontWeight.Bold)
}
.id("base_title_right_container")
.alignRules({
bottom: { anchor: "__container__", align: VerticalAlign.Bottom },
right: { anchor: "__container__", align: HorizontalAlign.End }
})
// .backgroundColor('#00ff00')
Row(){
Text('中间内容')
.fontSize(10)
.fontWeight(FontWeight.Bold)
}.alignRules({
bottom: { anchor: "__container__", align: VerticalAlign.Bottom },
left: { anchor: "base_title_left_container", align: HorizontalAlign.End },
right: { anchor: "base_title_right_container", align: HorizontalAlign.Start }
}).backgroundColor('#ff0000')
}
.height('40')
.width('100%')
.backgroundColor('#ccc')
.margin({top:100})
}
Effect:
The middle content disappears. After adding a background color to the right container:
It is found that the right layout occupies the entire width. Why?
The issue is that Flex cannot adapt to child node sizes. Switch to the Row container instead:
build() {
RelativeContainer() {
Text('左侧')
.fontSize(10)
.fontWeight(FontWeight.Bold)
.id("base_title_left_container")
.alignRules({
bottom: { anchor: "__container__", align: VerticalAlign.Bottom },
left: { anchor: "__container__", align: HorizontalAlign.Start }
})
Row() {
Text('右侧2')
.fontSize(10)
.fontWeight(FontWeight.Bold)
Text('右侧1')
.fontSize(10)
.fontWeight(FontWeight.Bold)
}
.id("base_title_right_container")
.alignRules({
bottom: { anchor: "__container__", align: VerticalAlign.Bottom },
right: { anchor: "__container__", align: HorizontalAlign.End }
})
.backgroundColor('#00ff00')
Row(){
Text('中间内容')
.fontSize(10)
.fontWeight(FontWeight.Bold)
}.alignRules({
bottom: { anchor: "__container__", align: VerticalAlign.Bottom },
left: { anchor: "base_title_left_container", align: HorizontalAlign.End },
right: { anchor: "base_title_right_container", align: HorizontalAlign.Start }
}).backgroundColor('#ff0000')
}
.height('40')
.width('100%')
.backgroundColor('#ccc')
.margin({top:100})
}
The effect returns to normal:
Summary
This article introduces the characteristics and attributes of HarmonyOS's RelativeContainer through the layout implementation of the common TitleBar control. It also highlights the difference in adaptive child node capabilities between Flex and Row: Row can adapt to child node widths, while Flex will fill the entire parent layout width.
Top comments (0)