Read the original article:How to Center Grid Component Items Vertically
Requirement Description
The Grid component cannot simultaneously support all three of the following: 100% height, child component centering, and drag events.
Specifically:
- When height is set to 100%, dragging works, but the child items are not vertically centered.
- If you want the items centered, you must set
rowsTemplateandcolumnsTemplate, but then the drag animation is lost.
build() {
RelativeContainer() {
Grid() {
ForEach(this.bottomNavList, (item: TabItem) => {
GridItem() {
this.BottomNavItem(item)
}
})
}
.height('100%')
.layoutWeight(1)
.align(Alignment.Center)
.rowsTemplate('1fr')
.columnsTemplate('1fr 1fr 1fr 1fr 1fr')
.maxCount(0)
.onItemDragStart((_, selectItemIndex: number) => {
console.info('itemIndex:' + selectItemIndex)
return this.BottomNavItem(this.bottomNavList[selectItemIndex])
})
.onItemDrop((_, itemIndex: number, insertIndex: number) => {
this.swapBottomNavItemPosition(itemIndex, insertIndex);
})
.editMode(true)
.backgroundColor(Color.Green)
.supportAnimation(true)
.align(Alignment.Center)
.padding({
left: 10,
right: 10,
bottom: 10 // Keep shadows from being cut off
})
.alignRules({
left: { anchor: '__container__', align: HorizontalAlign.Start },
center: { anchor: '__container__', align: VerticalAlign.Center }
})
Row() {
Text('Long press to drag and reorder')
.fontSize(20)
.fontColor(Color.Black)
}
.width('100%')
.justifyContent(FlexAlign.Center)
.padding({ bottom: 0 })
.alignRules({
left: { anchor: '__container__', align: HorizontalAlign.Start },
bottom: { anchor: '__container__', align: VerticalAlign.Bottom }
})
}
.padding({ bottom: 8 })
.height('100%')
}
Background Knowledge
- The Grid component is a grid container that only supports GridItem child components.
Its properties support general attributes, scrollable component common attributes, and attributes like
columnsTemplateandrowsTemplate. -
columnsTemplate defines the number of columns, fixed column width, or minimum column width. If not set, it defaults to one column.
When set to
'0fr', the column width is 0, and its GridItem will not be displayed. -
supportAnimation determines whether animation is supported. Animation is supported only in scroll mode (when only one of
rowsTemplateorcolumnsTemplateis set).
Implementation Steps
You can add an extra GridItem child and set its component height to 100%, with its width set to 0fr.
This ensures that other GridItem components are vertically centered while keeping the drag animation enabled.
Code Snippet / Configuration
class TabItem {
icon: string = ''
appletName: string = ''
canDelete: boolean = false
constructor(icon: string, appletName: string, canDelete: boolean) {
this.icon = icon;
this.appletName = appletName;
this.canDelete = canDelete;
}
}
@Entry
@Component
struct Index {
longPressTimer: number = 0;
@State bottomNavList: TabItem[] = [
new TabItem('app.media.startIcon', 'Home', false),
new TabItem('app.media.startIcon', 'Message', false),
new TabItem('app.media.startIcon', 'Contacts', false),
new TabItem('app.media.startIcon', 'Settings', false),
new TabItem('app.media.startIcon', 'Mine1', false),
]
$bottomNavList(list: TabItem[]) {
this.bottomNavList = list;
}
swapBottomNavItemPosition(itemIndex: number, insertIndex: number) {
let copyList = this.bottomNavList;
let tmpBottomNavItem = copyList.splice(itemIndex, 1);
copyList.splice(insertIndex, 0, tmpBottomNavItem[0]);
this.$bottomNavList(copyList);
}
build() {
RelativeContainer() {
Grid() {
GridItem() { // Add a separate GridItem at the beginning, set its height to 100%
Column()
.height('100%')
}
ForEach(this.bottomNavList, (item: TabItem) => {
GridItem() {
this.BottomNavItem(item)
}
})
}
.height('100%')
.layoutWeight(1)
.align(Alignment.Center)
.columnsTemplate('0fr 1fr 1fr 1fr 1fr 1fr') // The first GridItem has width 0fr, so it’s invisible
.maxCount(0)
.onItemDragStart((_, selectItemIndex: number) => {
console.info('itemIndex:' + selectItemIndex)
return this.BottomNavItem(this.bottomNavList[selectItemIndex - 1]) // Adjust index by -1 for correct display during drag
})
.onItemDrop((_, itemIndex: number, insertIndex: number) => {
this.swapBottomNavItemPosition(itemIndex - 1, insertIndex - 1); // Adjust index by -1 for correct reordering
})
.editMode(true)
.backgroundColor(Color.Green)
.supportAnimation(true)
.align(Alignment.Center)
.padding({
left: 10,
right: 10,
bottom: 10 // Keep shadows from being cut off
})
.alignRules({
left: { anchor: '__container__', align: HorizontalAlign.Start },
center: { anchor: '__container__', align: VerticalAlign.Center }
})
Row() {
Text('Long press\n to drag and reorder')
.fontSize(20)
.fontColor(Color.Black)
}
.width('100%')
.justifyContent(FlexAlign.Center)
.padding({ bottom: 32 })
.alignRules({
left: { anchor: '__container__', align: HorizontalAlign.Start },
bottom: { anchor: '__container__', align: VerticalAlign.Bottom }
})
}
.padding({ bottom: 8 })
.height('100%')
}
// Bottom Tab subcomponent
@Builder
BottomNavItem(botttomNav: TabItem) {
Column() {
// Purpose: Add padding to prevent shadow clipping during drag
Stack({ alignContent: Alignment.TopEnd }) {
Column({ space: 3 }) {
Image(botttomNav.icon)
.width(24)
.aspectRatio(1)
.draggable(false) // Disable XiaoYi assistant
Text(botttomNav.appletName)
.fontSize(6)
.fontColor(Color.Black)
}
.aspectRatio(1)
.justifyContent(FlexAlign.Center)
.shadow({ radius: 30, color: 'rgba(0,0,0,0.22)' })
.borderRadius(12)
.backgroundColor(Color.White)
.margin({ top: 2, right: 2 })
.padding({
left: 5,
top: 0,
right: 5
})
Image($r('app.media.startIcon'))
.width(24)
.aspectRatio(1)
.visibility(botttomNav.canDelete ? Visibility.Visible : Visibility.None)
}
}
.layoutWeight(1)
.padding({
left: 2,
right: 2,
bottom: 2,
top: 2
})
}
}
Note:
This method essentially adds one GridItem as a placeholder to occupy full height, ensuring that other GridItem components remain vertically centered.
Because of this, the drag index values must be adjusted by -1, as shown in the example above.
Test Results
Limitations or Considerations
This example supports API Version 20 Release and above.
It supports HarmonyOS 6.0.0 Beta2 SDK and above.
It must be compiled with DevEco Studio 6.0.0 Beta2 or later.

Top comments (0)