HarmonyOS鸿蒙Next中怎样使用layoutWeight和Grid实现弹性布局?
HarmonyOS鸿蒙Next中怎样使用layoutWeight和Grid实现弹性布局? 应用需要适配不同尺寸的设备(手机、平板、折叠屏),如何实现响应式布局?如何使用layoutWeight和Grid实现弹性布局?
1、Grid网格容器,由“行”和“列”分割的单元格所组成,通过指定“项目”所在的单元格做出各种各样的布局。
2、layoutWeight属性:设置组件的布局权重,使组件在父容器(Row/Column/Flex)的主轴方向按照权重分配尺寸。使得子元素自适应占满剩余空间。
所以Grid需要搭配Row或者Column或者Flex才能使用layoutWeight进行布局使用;
示例:
struct GridExample {
build() {
Grid() {
GridItem() {
Column() {
Text('权重:1')
}
.layoutWeight(1) // 分配1份剩余空间
.backgroundColor(Color.Green)
}
GridItem() {
Column() {
Text('权重:2')
}
.layoutWeight(2) // 分配2份剩余空间(占比更大)
.backgroundColor(Color.Blue)
}
}
.height('100%')
}
}
若需实现更精细的响应式效果(如根据屏幕尺寸动态调整列数),建议结合GridRow组件的断点能力(如breakpoints: ['320vp', '600vp'])同步优化。
更多关于HarmonyOS鸿蒙Next中怎样使用layoutWeight和Grid实现弹性布局?的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
解决方案
/**
* 响应式统计卡片
*/
@Component
struct ResponsiveStatsCard {
@State stats: Array<{title: string, value: string, color: string}> = [
{ title: '总收入', value: '¥5000', color: '#ff6b6b' },
{ title: '总支出', value: '¥3000', color: '#67c23a' },
{ title: '净收益', value: '¥2000', color: '#409eff' },
{ title: '记录数', value: '50笔', color: '#e6a23c' }
];
build() {
// 使用Grid实现响应式布局
Grid() {
ForEach(this.stats, (stat: any) => {
GridItem() {
Column({ space: 8 }) {
Text(stat.value)
.fontSize(24)
.fontWeight(FontWeight.Bold)
.fontColor(stat.color)
Text(stat.title)
.fontSize(14)
.fontColor('#999')
}
.width('100%')
.padding(20)
.backgroundColor(Color.White)
.borderRadius(12)
}
})
}
.columnsTemplate('1fr 1fr') // 2列布局
.rowsGap(12)
.columnsGap(12)
.width('100%')
.padding(16)
}
}
/**
* 弹性权重布局
*/
@Component
struct FlexWeightLayout {
build() {
Row() {
// 左侧固定宽度
Column() {
Text('固定区域')
.fontSize(16)
}
.width(100)
.height('100%')
.backgroundColor('#e3f2fd')
// 中间弹性区域
Column() {
Text('弹性区域')
.fontSize(16)
}
.layoutWeight(1) // 占据剩余空间
.height('100%')
.backgroundColor('#fff3e0')
// 右侧固定宽度
Column() {
Text('固定区域')
.fontSize(16)
}
.width(100)
.height('100%')
.backgroundColor('#f3e5f5')
}
.width('100%')
.height(200)
}
}
/**
* 媒体查询适配
*/
import { mediaQuery } from '@kit.ArkUI';
@Entry
@Component
struct AdaptiveLayout {
@State isTablet: boolean = false;
private listener: mediaQuery.MediaQueryListener = mediaQuery.matchMediaSync('(min-width: 600vp)');
aboutToAppear() {
this.listener.on('change', (result: mediaQuery.MediaQueryResult) => {
this.isTablet = result.matches;
});
this.isTablet = this.listener.matches;
}
aboutToDisappear() {
this.listener.off('change');
}
build() {
Column() {
if (this.isTablet) {
// 平板布局:左右分栏
Row() {
// 左侧列表
List() {
ForEach([1,2,3,4,5], (item: number) => {
ListItem() {
Text(`项目 ${item}`)
.width('100%')
.padding(16)
}
})
}
.width('40%')
.backgroundColor('#f5f5f5')
// 右侧详情
Column() {
Text('详情内容')
.fontSize(20)
}
.layoutWeight(1)
.backgroundColor(Color.White)
}
.width('100%')
.height('100%')
} else {
// 手机布局:单列
List() {
ForEach([1,2,3,4,5], (item: number) => {
ListItem() {
Text(`项目 ${item}`)
.width('100%')
.padding(16)
}
})
}
.width('100%')
.height('100%')
}
}
}
}
/**
* GridRow响应式布局
*/
@Component
struct GridRowDemo {
build() {
GridRow({
columns: { sm: 4, md: 8, lg: 12 }, // 不同断点的列数
gutter: { x: 12, y: 12 }, // 间距
breakpoints: { value: ['320vp', '600vp', '840vp'] } // 断点
}) {
// 小屏占4列,中屏占4列,大屏占3列
GridCol({ span: { sm: 4, md: 4, lg: 3 } }) {
Column() {
Text('卡片1')
}
.width('100%')
.height(100)
.backgroundColor('#e3f2fd')
.borderRadius(8)
}
GridCol({ span: { sm: 4, md: 4, lg: 3 } }) {
Column() {
Text('卡片2')
}
.width('100%')
.height(100)
.backgroundColor('#fff3e0')
.borderRadius(8)
}
GridCol({ span: { sm: 4, md: 4, lg: 3 } }) {
Column() {
Text('卡片3')
}
.width('100%')
.height(100)
.backgroundColor('#f3e5f5')
.borderRadius(8)
}
GridCol({ span: { sm: 4, md: 4, lg: 3 } }) {
Column() {
Text('卡片4')
}
.width('100%')
.height(100)
.backgroundColor('#e0f7fa')
.borderRadius(8)
}
}
.width('100%')
.padding(16)
}
}
原理解析
1. layoutWeight弹性布局
.layoutWeight(1) // 占据剩余空间
- 多个组件设置layoutWeight会按比例分配
- 常用于左右分栏、上下分栏
2. Grid网格布局
.columnsTemplate('1fr 1fr') // 2列等宽
.columnsTemplate('100px 1fr 2fr') // 固定+弹性
- fr单位表示剩余空间的份数
- 支持固定宽度和弹性宽度混合
3. 媒体查询
mediaQuery.matchMediaSync('(min-width: 600vp)')
- 监听屏幕宽度变化
- 根据断点切换布局
4. GridRow响应式
columns: { sm: 4, md: 8, lg: 12 }
- 自动根据屏幕宽度选择列数
- span设置占据的列数
最佳实践
- 优先使用layoutWeight: 简单场景用layoutWeight
- Grid适合卡片: 统计卡片、功能入口用Grid
- 媒体查询: 大幅布局变化用媒体查询
- GridRow: 复杂响应式用GridRow
- 断点设置: 320vp(手机)、600vp(平板)、840vp(PC)
避坑指南
- layoutWeight父容器: 父容器必须有明确宽度/高度
- Grid高度: Grid需要设置高度或使用layoutWeight
- 媒体查询内存: 组件销毁时off监听
- GridRow嵌套: 避免GridRow嵌套GridRow
- 性能: 媒体查询变化时避免大量重绘
在HarmonyOS Next中,使用layoutWeight和Grid实现弹性布局的方法如下:
-
layoutWeight:在
<Row>或<Column>容器中,为子组件设置layoutWeight属性。该属性值表示子组件在主轴方向上的权重,系统会根据权重分配剩余空间。例如:<Row> <Text layoutWeight="1">组件1</Text> <Text layoutWeight="2">组件2</Text> </Row> -
Grid:使用
<Grid>容器,通过columnsTemplate和rowsTemplate定义网格列和行的数量与尺寸。例如:<Grid columnsTemplate="1fr 2fr 1fr" rowsTemplate="1fr 2fr"> <!-- 子组件 --> </Grid>
结合两者时,可在Grid的单元格内使用带权重的Row或Column实现弹性布局。
在HarmonyOS Next中,使用layoutWeight和Grid实现弹性布局是适配多尺寸设备的核心方案。
1. 使用layoutWeight实现弹性布局
layoutWeight用于在容器内按比例分配剩余空间,非常适合构建响应式布局。
示例:水平等分三栏
Row() {
Text('Left')
.layoutWeight(1)
.backgroundColor(Color.Red)
Text('Center')
.layoutWeight(2) // 占据Left两倍宽度
.backgroundColor(Color.Blue)
Text('Right')
.layoutWeight(1)
.backgroundColor(Color.Green)
}
.width('100%')
.height(100)
关键点:
- 父容器需明确尺寸(如
width('100%')) - 子组件按
layoutWeight比例分配剩余空间 - 支持Row和Column布局方向
2. 使用Grid实现网格布局
Grid容器提供更灵活的网格系统,支持跨设备适配。
基础网格示例:
Grid() {
GridItem() { Text('Item1') }
GridItem() { Text('Item2') }
GridItem() { Text('Item3') }
GridItem() { Text('Item4') }
}
.columnsTemplate('1fr 1fr 1fr 1fr') // 4等分列
.rowsTemplate('100px')
.columnsGap(10)
.rowsGap(10)
3. 响应式布局实现
结合布局断点实现多设备适配:
@Entry
@Component
struct ResponsiveLayout {
@State currentBreakpoint: string = 'md'
build() {
Column() {
// 根据断点调整布局
if (this.currentBreakpoint === 'sm') {
this.smallScreenLayout()
} else if (this.currentBreakpoint === 'md') {
this.mediumScreenLayout()
} else {
this.largeScreenLayout()
}
}
.onAreaChange((oldArea, newArea) => {
// 监听屏幕尺寸变化
this.updateBreakpoint(newArea.width)
})
}
// 小屏布局(手机)
smallScreenLayout() {
Column() {
Text('Header').layoutWeight(1)
Text('Content').layoutWeight(3)
Text('Footer').layoutWeight(1)
}
}
// 中屏布局(平板)
mediumScreenLayout() {
Grid() {
GridItem() { Text('Sidebar') }
.columnStart(0)
.columnEnd(1)
GridItem() { Text('Main') }
.columnStart(1)
.columnEnd(3)
}
.columnsTemplate('1fr 2fr')
.rowsTemplate('100%')
}
}
4. 实用技巧
- 混合使用:在GridItem内部使用
layoutWeight进行精细控制 - 断点设置:基于典型设备宽度设置断点(sm: <600px, md: 600-840px, lg: >840px)
- 栅格系统:使用12列栅格提高布局一致性
这种组合方案能有效应对手机、平板、折叠屏等不同设备的布局适配需求。

