网格(Grid)

网格布局是由“行”和“列”分割的单元格所组成,网格布局具有较强的页面均分能力,子组件占比控制能力,是一种重要自适应布局,其使用场景有九宫格图片展示、日历、计算器等。

ArkUI提供了Grid容器组件和子组件GridItem,用于构建网格布局。Grid用于设置网格布局相关参数,GridItem定义子组件相关特征。Grid组件支持使用条件渲染循环渲染懒加载等方式生成子组件。

Grid组件为网格容器,其子组件必须是GridItem组件,两者的关系如下图所示。

在这里插入图片描述

基本使用

在使用GridGridItem进行布局时需要设置行和列的数量以及占比,通过.rowsTemplate("1fr 1fr 1fr").columnsTemplate('1fr 2fr 1fr')和进行列数量和占比设置,其中有几个fr就表示几列或者几行,前面的数字表示单元格占比。

如下图所示

在这里插入图片描述

代码如下

@Entry
@Component
struct Index {
  @State gridItemList: number[] = [1, 2, 3, 4, 5, 6, 7, 8, 9]

  build() {
    Column() {
      Grid() {
        ForEach(this.gridItemList, (item: number) => {
          GridItem() {
            Row()
              .width("100%").height("100%").backgroundColor('#A4C2E2')
          }
        })
      }
      .columnsTemplate('1fr 2fr 1fr') //3列,占比为1:2:1
      .rowsTemplate("1fr 1fr 1fr") //3行,占比1:1:1
      .columnsGap(10)	//列间距
      .rowsGap(10)		//行间距
    }.width("100%")
    .backgroundColor("#dedede")
    .padding(10)
  }
}

[!Warning]

  • 如果Grid组件设置了宽高属性,则其尺寸为设置值。如果没有设置宽高属性,Grid组件的尺寸默认适应其父组件的尺寸。

  • 行、列数量与占比同时设置:Grid只展示固定行列数的元素,其余元素不展示,且Grid不可滚动。(推荐使用该种布局方式)

  • 只设置行、列数量与占比中的一个:元素按照设置的方向进行排布,超出的元素可通过滚动的方式展示。

  • 行列数量与占比都不设置:元素在布局方向上排布,其行列数由布局方向、单个网格的宽高等多个属性共同决定。超出行列容纳范围的元素不展示,且Grid不可滚动。

采用下面配置测试以上情况,将Grid父组件的尺寸设置为300x300Grid宽度自适应父组件高度,每一个GridItem尺寸设置为86.6x86.6,行间距、列间距设置为10,测试结果如下:

在这里插入图片描述

设置主轴排列方向

使用Grid构建网格布局时,若没有设置行列数量与占比,可以通过layoutDirection设置网格布局的主轴方向,决定子组件的排列方式,此时可以结合minCountmaxCount属性来约束主轴方向上的网格数量。

  • 当前layoutDirection设置为Row时,先从左到右排列,排满一行再排下一行。
  • 当前layoutDirection设置为Column时,先从上到下排列,排满一列再排下一列,

如上图所示。此时,将maxCount属性设为3,表示主轴方向上最大显示的网格单元数量为3

在这里插入图片描述

@Entry
@Component
struct Index {
  @State gridItemList: number[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
  
  build() {
    Column() {
      Grid() {
        ForEach(this.gridItemList, (item: number) => {
          GridItem() {
            Text(`${item}`)
              .width(86.6).height(86.6).backgroundColor('#A4C2E2').textAlign(TextAlign.Center)
          }
        })
      }
      .layoutDirection(GridDirection.Row) //主轴水平向右(排完一排再排下一排)
      //.layoutDirection(GridDirection.Column)  //主轴向下(排完一列再排下一列)
      .maxCount(3)  //主轴方向的最大个数
      .columnsGap(10) //列间距
      .rowsGap(10) //行间距
    }
    .width(300)
    .backgroundColor("#dedede")
    .padding(10)
  }
}

[!Warning]

layoutDirection属性仅在不设置rowsTemplatecolumnsTemplate时生效。

合并单元格

Grid组件中,可以通过创建Grid时传入合适的GridLayoutOptions实现如图所示的单个网格横跨多行或多列的场景。

参数:

  • 通过onGetRectByIndex返回的[rowStart,columnStart,rowSpan,columnSpan]来实现跨行跨列布局。

    • rowStartcolumnStart属性表示指定当前元素起始行号和起始列号

    • rowSpancolumnSpan属性表示指定当前元素的占用行数和占用列数。

示例:

例如计算器的按键布局就是常见的不均匀网格布局场景。如下图,计算器中的按键“0”“=”,按键“0”横跨标号为0、1列,按键“=”横跨标号为3、4行。每一个GridItem都有一个索引标号,其行列标号从0开始,依次编号。

在这里插入图片描述

@Entry
@Component
struct Index {
  //按键显示的文本
  @State gridItemList: string[] = [
    'CE', 'C', '/', 'X',
    '7', '8', '9', '-',
    '4', '5', '6', '+',
    '1', '2', '3', '=',
    '0', '.'
  ]
    
  //滚动条,需要和Grid绑定
  private scroller: Scroller = new Scroller()
    
  //网格布局
  private gridLayoutOptions: GridLayoutOptions = {
    regularSize: [1, 1],
    onGetRectByIndex: (index: number) => {
      if (index === 15) {
        return [4, 0, 1, 2]
      }
      if (index === 16) {
        return [3, 2, 2, 1]
      }
      
      return [0, 0, 1, 1]
    }
  }

  build() {
    Column({ space: 10 }) {
      Row() {
        Text("123456")
          .textAlign(TextAlign.End)
          .fontSize(30)
          .fontWeight(FontWeight.Bolder)
          .fontColor(Color.White)
      }
      .width("100%")
      .layoutWeight(1)
      .justifyContent(FlexAlign.End)
      .alignItems(VerticalAlign.Bottom)
      .backgroundColor("#ffa0bcb4")
      .padding(10)
      .borderRadius(10)

      Grid(this.scroller, this.gridLayoutOptions) {
        ForEach(this.gridItemList, (item: string) => {
          GridItem() {
            Text(item)
              .width("100%")
              .height("100%")
              .fontColor(Color.White)
              .fontSize(26)
              .textAlign(TextAlign.Center)
              .fontWeight(FontWeight.Bolder)
              .backgroundColor(Color.Grey)
              .borderRadius(10)
          }
        })
      }
      .width("100%")
      .height(460)
      .columnsGap(10)
      .rowsGap(10)
      .columnsTemplate('1fr 1fr 1fr 1fr')
      .rowsTemplate('1fr 1fr 1fr 1fr 1fr')
    }
    .width("100%")
    .height("100%")
    .backgroundColor("#dedede")
    .padding(10)
  }
}

控制滚动位置

Grid组件初始化时,可以绑定一个Scroller对象,用于进行滚动控制,例如通过Scroller对象的scrollPage方法进行翻页。

在这里插入图片描述

@Entry
@Component
struct Index {
  @State gridItemList: number[] = []
  private scroller: Scroller = new Scroller();

  aboutToAppear(): void {
    for (let i = 1; i <= 30; i++) {
      this.gridItemList.push(i)
    }

    for (let i = 1; i <= 31; i++) {
      this.gridItemList.push(i)
    }
  }

  build() {
    Column({ space: 10 }) {
      Grid(this.scroller) {
        ForEach(this.gridItemList, (item: number) => {
          GridItem() {
            Text(`${item}`)
              .width(30).height(30).backgroundColor('#A4C2E2').textAlign(TextAlign.Center)
          }
        })
      }
      .width(300)
      .height(200)
      .backgroundColor("#dedede")
      .columnsTemplate("1fr 1fr 1fr 1fr 1fr 1fr 1fr")
      .columnsGap(10) //列间距
      .rowsGap(10) //行间距

      Row() {
        Button("上一页").onClick(() => {
          this.scroller.scrollPage({
            next: false
          })
        })
        Button("下一页").onClick(() => {
          this.scroller.scrollPage({
            next: true
          })
        })
      }
    }
    .padding(10)
  }
}

到此,Grid网格布局的使用就介绍完了🎉🎉🎉。

对鸿蒙感兴趣的同学,可以考免费取鸿蒙开发者认证

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐