HarmonyOS鸿蒙Next中怎样使用layoutWeight和Grid实现弹性布局?

HarmonyOS鸿蒙Next中怎样使用layoutWeight和Grid实现弹性布局? 应用需要适配不同尺寸的设备(手机、平板、折叠屏),如何实现响应式布局?如何使用layoutWeight和Grid实现弹性布局?

4 回复

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设置占据的列数

最佳实践

  1. 优先使用layoutWeight: 简单场景用layoutWeight
  2. Grid适合卡片: 统计卡片、功能入口用Grid
  3. 媒体查询: 大幅布局变化用媒体查询
  4. GridRow: 复杂响应式用GridRow
  5. 断点设置: 320vp(手机)、600vp(平板)、840vp(PC)

避坑指南

  1. layoutWeight父容器: 父容器必须有明确宽度/高度
  2. Grid高度: Grid需要设置高度或使用layoutWeight
  3. 媒体查询内存: 组件销毁时off监听
  4. GridRow嵌套: 避免GridRow嵌套GridRow
  5. 性能: 媒体查询变化时避免大量重绘

在HarmonyOS Next中,使用layoutWeight和Grid实现弹性布局的方法如下:

  1. layoutWeight:在<Row><Column>容器中,为子组件设置layoutWeight属性。该属性值表示子组件在主轴方向上的权重,系统会根据权重分配剩余空间。例如:

    <Row>
      <Text layoutWeight="1">组件1</Text>
      <Text layoutWeight="2">组件2</Text>
    </Row>
    
  2. Grid:使用<Grid>容器,通过columnsTemplaterowsTemplate定义网格列和行的数量与尺寸。例如:

    <Grid columnsTemplate="1fr 2fr 1fr" rowsTemplate="1fr 2fr">
      <!-- 子组件 -->
    </Grid>
    

结合两者时,可在Grid的单元格内使用带权重的Row或Column实现弹性布局。

在HarmonyOS Next中,使用layoutWeightGrid实现弹性布局是适配多尺寸设备的核心方案。

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列栅格提高布局一致性

这种组合方案能有效应对手机、平板、折叠屏等不同设备的布局适配需求。

回到顶部