2.9、创建网格(Grid/GridItem)

概述

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

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

布局与约束

Grid组件为网格容器,其中容器内每一个条目对应一个GridItem组件,如下图所示。
在这里插入图片描述
对应代码

Column() {
   Text("Grid")
       .textAlign(TextAlign.Center)
       .width(100)
   Grid() {
     ForEach(this.gridItems, (item:string)=>{
       GridItem() {
         Text(item)
           .textAlign(TextAlign.Center)
           .backgroundColor('#fffbf0')
           .border({color:'#ffc100', width: 1})
           .width(100)
           .height(100)
       }
     })
   }
   .padding({left:10, right:10, top: 10, bottom: 10})
   .maxCount(3)
   .layoutDirection(GridDirection.Row)
   .columnsGap(10)
   .rowsGap(15)
 }
 .padding({top: 10})
 .margin({left:5})
 .border({color:'#2586d9', width:1})
 .backgroundColor('#f0faff')

设置排列方式

设置行列数量与占比

通过设置行列数量与尺寸占比可以确定网格布局的整体排列方式。Grid组件提供了rowsTemplate和columnsTemplate属性用于设置网格布局行列数量与尺寸占比。

rowsTemplate和columnsTemplate属性值是一个由多个空格和’数字+fr’间隔拼接的字符串,fr的个数即网格布局的行或列数,fr前面的数值大小,用于计算该行或列在网格布局宽度上的占比,最终决定该行或列的宽度。

Grid() {
  ...
}
.rowsTemplate('1fr 1fr 1fr')
.columnsTemplate('1fr 2fr 1fr')

在这里插入图片描述
对应代码

@Entry
@Component
struct RowColumnPercentPage {
  @State calcItems: Array<string> = []

  aboutToAppear() {
    for (let index = 0; index < 9; index++) {
      this.calcItems.push(`Item${index+1}`)
    }
  }

  build() {
    Navigation() {
      Grid() {
        ForEach(this.calcItems, (item:string)=>{
           GridItem() {
             Text(item)
           }
          .backgroundColor('#9dc3e6')
        })
      }
      .rowsTemplate('1fr 1fr 1fr')
      .columnsTemplate('1fr 2fr 1fr')
      .columnsGap(5)
      .rowsGap(5)
      .padding({ left: 5, right: 5, top: 5 })
      .width('100%')
      .height('50%')
      .backgroundColor(0xF0F0F0)
    }
    .title('设置行列数量与占比')
    .titleMode(NavigationTitleMode.Mini)
  }
}

设置子组件所占行列数

除了大小相同的等比例网格布局,由不同大小的网格组成不均匀分布的网格布局场景在实际应用中十分常见,如下图所示。在Grid组件中,通过设置GridItem的rowStart、rowEnd、columnStart和columnEnd可以实现如图所示的单个网格横跨多行或多列的场景。
在这里插入图片描述
对应代码

Grid() {
   ForEach(this.calcItems, (item:string)=>{
      if (item === "3") {
        GridItem() {
          Text(item)
        }
        .columnStart(3)
        .columnEnd(4)
        .backgroundColor('#9dc3e6')
      } else if (item === "4") {
        GridItem() {
          Text(item)
        }
        .rowStart(1)
        .rowEnd(2)
        .backgroundColor('#9dc3e6')
      } else if (item === "8") {
        GridItem() {
          Text(item)
        }
        .columnStart(1)
        .columnEnd(3)
        .backgroundColor('#9dc3e6')
      } else {
        GridItem() {
          Text(item)
        }.backgroundColor('#9dc3e6')
      }
   })
 }
 .rowsTemplate('1fr 1fr 1fr')
 .columnsTemplate('1fr 1fr 1fr 1fr')
 .columnsGap(5)
 .rowsGap(5)
 .padding({ left: 5, right: 5, top: 5 })
 .width('100%')
 .height(250)
 .backgroundColor(0xF0F0F0)

例如计算器的按键布局就是常见的不均匀网格布局场景。如下图,计算器中的按键“0”和“=”,按键“0”横跨第一、二两列,按键“=”横跨第五、六两行。使用Grid构建的网格布局,其行列标号从1开始,依次编号。
在这里插入图片描述
在单个网格单元中,rowStart和rowEnd属性表示指定当前元素起始行号和终点行号,columnStart和columnEnd属性表示指定当前元素的起始列号和终点列号。

所以“0”按键横跨第一列和第二列,只要将“0”对应GridItem的columnStart和columnEnd设为1和2,将“=”对应GridItem的的rowStart和rowEnd设为5和6即可。

实现的代码

 
@Entry
@Component
struct CalculatorPage {
  calcItems: Array<string> = [
    "CE", "C", "/", "X",
    "7", "8", "9", "-",
    "4", "5", "6", "+",
    "1", "2", "3", "=",
    "0", "."
  ]

  build() {
    Navigation() {
      Grid() {
        GridItem() {
          Text('0')
            .fontSize(30)
            .backgroundColor('#b2b1b6')
            .width('100%')
            .height('100%')
            .textAlign(TextAlign.End)
            .padding({ right: 10 })
            .borderRadius(5)
        }
        .columnStart(1)
        .columnEnd(4)

        ForEach(this.calcItems, (item: string) => {
          if (item === '0') {
            GridItem() {
              Text(item)
            }
            .borderRadius(5)
            .backgroundColor('#b2b1b6')
            .columnStart(1)
            .columnEnd(2)
          } else if (item === '=') {
            GridItem() {
              Text(item)
            }
            .borderRadius(5)
            .backgroundColor('#b2b1b6')
            .rowStart(4)
            .rowEnd(5)
          } else {
            GridItem() {
              Text(item)
            }
            .borderRadius(5)
            .backgroundColor('#b2b1b6')
          }
        })
      }
      .columnsTemplate('1fr 1fr 1fr 1fr')
      .rowsTemplate('2fr 1fr 1fr 1fr 1fr 1fr')
      .columnsGap(5)
      .rowsGap(5)
      .padding({ left: 5, right: 5, top: 5 })
      .width('100%')
      .backgroundColor(0xF0F0F0)
      .height('70%')
    }
    .title('计算器')
    .titleMode(NavigationTitleMode.Mini)
  }
}

设置主轴方向

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

Grid() {
  ...
}
.layoutDirection(GridDirection.Row)

在这里插入图片描述

Grid() {
  ...
}
.layoutDirection(GridDirection.Column)

在这里插入图片描述

对应完整代码

@Entry
@Component
struct LayoutDirectionPage {
  @State calcItems: Array<string> = []

  aboutToAppear() {
    for (let index = 0; index < 9; index++) {
      this.calcItems.push(`${index+1}`)
    }
  }

  build() {
    Navigation() {
      Grid() {
        ForEach(this.calcItems, (item:string)=>{
             GridItem() {
               Text(item)
             }
             .width('30%')
             .aspectRatio(1)
             .backgroundColor('#9dc3e6')
        })
      }
      .layoutDirection(GridDirection.Column)
      .columnsGap(5)
      .rowsGap(5)
      .maxCount(3)
      .backgroundColor(0xF0F0F0)
      .padding({ left: 15, right: 15, top: 15, bottom:15 })
      .width('100%')
    }
    .title('主轴方向')
    .titleMode(NavigationTitleMode.Mini)
  }
}

在网格布局中显示数据

网格布局采用二维布局的方式组织其内部元素,如下图所示。
在这里插入图片描述
对应代码

Grid() {
  GridItem() {
    Text('会议')
  }.backgroundColor('#9dc3e6')

  GridItem() {
    Text('签到')
  }.backgroundColor('#9dc3e6')

  GridItem() {
    Text('投票')
  }.backgroundColor('#9dc3e6')

  GridItem() {
    Text('打印')
  }.backgroundColor('#9dc3e6')
}
.rowsTemplate('1fr 1fr')
.columnsTemplate('1fr 1fr')
.backgroundColor('#e3e3e5')
.width('100%')
.aspectRatio(1)

设置行列间距

在这里插入图片描述
对应代码

Grid() {
   GridItem() {
     Text('会议')
   }.backgroundColor('#9dc3e6')

   GridItem() {
     Text('签到')
   }.backgroundColor('#9dc3e6')

   GridItem() {
     Text('投票')
   }.backgroundColor('#9dc3e6')

   GridItem() {
     Text('打印')
   }.backgroundColor('#9dc3e6')
}
.rowsTemplate('1fr 1fr')
.columnsTemplate('1fr 1fr')
.backgroundColor('#e3e3e5')
.width('100%')
.aspectRatio(1)
.columnsGap(5)
.rowsGap(5)

构建可滚动的网格布局

可滚动的网格布局常用在文件管理、购物或视频列表等页面中,如下图所示。在设置Grid的行列数量与占比时,如果仅设置行、列数量与占比中的一个,即仅设置 rowsTemplate 或仅设置 columnsTemplate 属性,网格单元按照设置的方向排列,超出Grid显示区域后,Grid拥有可滚动能力
在这里插入图片描述
如果设置的是 columnsTemplateGrid 的滚动方向为垂直方向;如果设置的是 rowsTemplateGrid 的滚动方向为水平方向。

对应代码


@Entry
@Component
struct HorizontalScrollPage {

  topScrollItem:Array<string> = []

  aboutToAppear() {
    for (let index = 0; index < 12; index++) {
      this.topScrollItem.push(`Item${index + 1}`)
    }
  }

  build() {
    Navigation() {
      Grid() {
        ForEach(this.topScrollItem, (item:string)=> {
          GridItem() {
            Text(item)
          }.backgroundColor('#9dc3e6')
          .width('25%')
        })
      }
      .rowsTemplate('1fr 1fr')
      .backgroundColor('#e3e3e5')
      .width('100%')
      .height(150)
      .columnsGap(5)
      .rowsGap(5)
    }
    .title('横向可滚动列表')
    .titleMode(NavigationTitleMode.Mini)
  }
}

控制滚动位置

在这里插入图片描述
对应代码

@Entry
@Component
struct ControlScrollPage {
  private scroller: Scroller = new Scroller()
  topScrollItem: Array<string> = []

  aboutToAppear() {
    for (let index = 0; index < 16; index++) {
      this.topScrollItem.push(`Item${index + 1}`)
    }
  }

  build() {
    Navigation() {
      Column() {
        Grid(this.scroller) {
          ForEach(this.topScrollItem, (item: string) => {
            GridItem() {
              Text(item)
            }.backgroundColor('#9dc3e6')
            .width('25%')
          })
        }
        .rowsTemplate('1fr 1fr')
        .backgroundColor('#e3e3e5')
        .width('100%')
        .height(150)

        Row({ space: 20 }) {
          Button('上一页')
            .onClick(() => {
              this.scroller.scrollPage({
                next: false,
              })
            })

          Button('下一页')
            .onClick(() => {
              this.scroller.scrollPage({
                next: true
              })
            })
        }.margin({top:10})
      }
    }
    .title('控制可滚动列表')
    .titleMode(NavigationTitleMode.Mini)
  }
}

上一篇 2.8、下拉刷新与上拉加载
下一篇 2.10、创建轮播(Swiper)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值