创建网格(Grid/GridItem)

目录

1、概述

2、布局与约束

3、设置排列方式

3.1设置行列数量与占比

3.2、设置子组件所占行列数

3.3、设置主轴方向

 3.4、在网格布局中显示数据

3.5、设置行列间距

4、构建可滚动的网格布局

5、实现简单的日历功能

6、性能优化


1、概述

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

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

2、布局与约束

        Grid组件为网格容器,其中容器内每一个条目对应一个GridItem组件,如下图所示。

图1 Grid与GridItem组件关系

说明

Grid的子组件必须是GridItem组件。

        网格布局是一种二维布局。Grid组件支持自定义行列数和每行每列尺寸占比、设置子组件横跨几行或者几列,同时提供了垂直和水平布局能力。当网格容器组件尺寸发生变化时,所有子组件以及间距会等比例调整,从而实现网格布局的自适应能力。根据Grid的这些布局能力,可以构建出不同样式的网格布局,如下图所示。

图2 网格布局

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

Grid组件根据行列数量与占比属性的设置,可以分为三种布局情况:

  • 行、列数量与占比同时设置:Grid只展示固定行列数的元素,其余元素不展示,且Grid不可滚动。(推荐使用该种布局方式)
  • 只设置行、列数量与占比中的一个:元素按照设置的方向进行排布,超出的元素可通过滚动的方式展示。
  • 行列数量与占比都不设置:元素在布局方向上排布,其行列数由布局方向、单个网格的宽高等多个属性共同决定。超出行列容纳范围的元素不展示,且Grid不可滚动。

3、设置排列方式

3.1设置行列数量与占比

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

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

图3 行列数量占比示例

        如上图所示,构建的是一个三行三列的的网格布局,其在垂直方向上分为三等份,每行占一份;在水平方向上分为四等份,第一列占一份,第二列占两份,第三列占一份。

        只要将rowsTemplate的值为'1fr 1fr 1fr',同时将columnsTemplate的值为'1fr 2fr 1fr',即可实现上述网格布局。

@Entry
@Component
struct GridLayoutPage1 {@State message: string = 'Hello World'build() {Row() {Column() {Grid() {GridItem() {Text(`1`).gridTextStyle("#1067c8ff")}GridItem() {Text(`2`).gridTextStyle("#2067c8ff")}GridItem() {Text(`3`).gridTextStyle("#3067c8ff")}GridItem() {Text(`4`).gridTextStyle("#4067c8ff")}GridItem() {Text(`5`).gridTextStyle("#5067c8ff")}GridItem() {Text(`6`).gridTextStyle("#6067c8ff")}GridItem() {Text(`7`).gridTextStyle("#7067c8ff")}GridItem() {Text(`8`).gridTextStyle("#8067c8ff")}GridItem() {Text(`9`).gridTextStyle("#9067c8ff")}}.rowsTemplate('1fr 1fr 1fr').columnsTemplate('1fr 2fr 1fr')}.width('100%')}.height('40%')}
}@Extend(Text) function gridTextStyle(value: ResourceColor) {.backgroundColor(value).width('100%').height('100%').fontSize(22).textAlign(TextAlign.Center)
}

        效果如下:

 

3.2、设置子组件所占行列数

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

图4 不均匀网格布局

        例如计算器的按键布局就是常见的不均匀网格布局场景。如下图,计算器中的按键“0”和“=”,按键“0”横跨第一、二两列,按键“=”横跨第五、六两行。使用Grid构建的网格布局,其行列标号从1开始,依次编号。

图5 计算器

        在单个网格单元中,rowStart和rowEnd属性表示指定当前元素起始行号和终点行号,columnStart和columnEnd属性表示指定当前元素的起始列号和终点列号。

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

        “=”按键横跨第五行和第六行,只要将将“=”对应GridItem的的rowStart和rowEnd设为5和6即可。

@Entry
@Component
struct CalculatorLayoutPage {@State message: string = 'Hello World'build() {Row() {Column() {Grid() {GridItem() {Text(`0`).gridTextStyle2("#1067c8ff").textAlign(TextAlign.End).padding(2)}.rowStart(0).rowEnd(1).columnStart(0).columnEnd(3)GridItem() {Text(`CE`).gridTextStyle2("#2067c8ff")}GridItem() {Text(`C`).gridTextStyle2("#3067c8ff")}GridItem() {Text(`/`).gridTextStyle2("#4067c8ff")}GridItem() {Text(`X`).gridTextStyle2("#5067c8ff")}GridItem() {Text(`7`).gridTextStyle2("#6067c8ff")}GridItem() {Text(`8`).gridTextStyle2("#7067c8ff")}GridItem() {Text(`9`).gridTextStyle2("#8067c8ff")}GridItem() {Text(`-`).gridTextStyle2("#9067c8ff")}GridItem() {Text(`4`).gridTextStyle2("#A067c8ff")}GridItem() {Text(`5`).gridTextStyle2("#B067c8ff")}GridItem() {Text(`6`).gridTextStyle2("#C067c8ff")}GridItem() {Text(`+`).gridTextStyle2("#D067c8ff")}GridItem() {Text(`1`).gridTextStyle2("#E067c8ff")}GridItem() {Text(`2`).gridTextStyle2("#F067c8ff")}GridItem() {Text(`3`).gridTextStyle2("#F167c8ff")}GridItem() {Text(`=`).gridTextStyle2("#F267c8ff")}.rowStart(5).rowEnd(6).columnStart(3).columnEnd(3)GridItem() {Text(`0`).gridTextStyle2("#F367c8ff")}.columnStart(0).columnEnd(1)GridItem() {Text(`.`).gridTextStyle2("#F467c8ff")}}.margin(12).rowsGap(12).columnsGap(8).rowsTemplate('1fr 1fr 1fr 1fr 1fr 1fr 1fr').columnsTemplate('1fr 1fr 1fr 1fr')}.width('100%')}.height('80%')}
}@Extend(Text) function gridTextStyle2(value: ResourceColor) {.backgroundColor(value).width('100%').height('100%').fontSize(40).textAlign(TextAlign.Center).borderRadius(5)
}

         效果如下:

3.3、设置主轴方向

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

图6 主轴方向示意图

        当前layoutDirection设置为Row时,先从左到右排列,排满一行再排一下一行。当前layoutDirection设置为Column时,先从上到下排列,排满一列再排一下一列,如上图所示。此时,将maxCount属性设为3,表示主轴方向上最大显示的网格单元数量为3。

@Entry
@Component
struct GridLayoutPage2 {@State array: Array<string> = ['1', '2', '3', '4', '5', '6', '7', '8', '9']build() {Row() {Column() {Grid() {ForEach(this.array, (item: string, index) => {GridItem() {Text(item).gridTextStyle_page2('#67c8ff')}}, item => item)}.rowsGap(12).columnsGap(8).margin(4).maxCount(3).minCount(3).layoutDirection(GridDirection.Column)}.width('100%')}.height('40%')}
}@Extend(Text) function gridTextStyle_page2(value: ResourceColor) {.backgroundColor(value).width('30%').height('30%').fontSize(22).textAlign(TextAlign.Center)
}

        效果如下:

说明

1. layoutDirection属性仅在不设置rowsTemplate和columnsTemplate时生效,此时元素在layoutDirection方向上排列。

2. 仅设置rowsTemplate时,Grid主轴为水平方向,交叉轴为垂直方向。

2. 仅设置columnsTemplate时,Grid主轴为垂直方向,交叉轴为水平方向。

 3.4、在网格布局中显示数据

        网格布局采用二维布局的方式组织其内部元素,如下图所示。

图7 通用办公服务

        Grid组件可以通过二维布局的方式显示一组GridItem子组件。

@Entry
@Component
struct GridLayoutPage3 {@State message: string = 'Hello World'build() {Row() {Column() {Grid() {GridItem() {Text('会议')}.backgroundColor('#1067c8ff')GridItem() {Text('签到')}.backgroundColor('#3067c8ff')GridItem() {Text('培训')}.backgroundColor('#5067c8ff')GridItem() {Text('打印')}.backgroundColor('#7067c8ff')}.rowsTemplate('1fr 1fr').columnsTemplate('1fr 1fr')}.width('100%').height('40%')}.height('100%').alignItems(VerticalAlign.Top)}
}

        对于内容结构相似的多个GridItem,通常更推荐使用循环渲染ForEach语句中嵌套GridItem的形式,来减少重复代码。

@Entry
@Component
struct GridLayoutPage3 {@State message: Bean[] = [{ title: '会议', color: '#1067c8ff' },{ title: '签到', color: '#3067c8ff' },{ title: '培训', color: '#5067c8ff' },{ title: '打印', color: '#7067c8ff' }]build() {Row() {Column() {Grid() {ForEach(this.message, (item: Bean) => {GridItem() {Text(item.title).backgroundColor(item.color).width('100%').height('100%').textAlign(TextAlign.Center)}}, item => JSON.stringify(item))}.rowsTemplate('1fr 1fr').columnsTemplate('1fr 1fr')}.width('100%').height('40%')}.height('100%').alignItems(VerticalAlign.Top)}
}class Bean {title: stringcolor: ResourceColor
}

        效果同上。

3.5、设置行列间距

        在两个网格单元之间的网格横向间距称为行间距,网格纵向间距称为列间距,如下图所示。

图8 网格的行列间距

        通过Grid的rowsGap和columnsGap可以设置网格布局的行列间距。

@Entry
@Component
struct GridLayoutPage3 {@State message: Bean[] = [{ title: '会议', color: '#1067c8ff' },{ title: '签到', color: '#3067c8ff' },{ title: '培训', color: '#5067c8ff' },{ title: '打印', color: '#7067c8ff' }]build() {Row() {Column() {Grid() {ForEach(this.message, (item: Bean) => {GridItem() {Text(item.title).backgroundColor(item.color).width('100%').height('100%').textAlign(TextAlign.Center)}}, item => JSON.stringify(item))}.rowsTemplate('1fr 1fr').columnsTemplate('1fr 1fr').rowsGap(10).columnsGap(10).margin(10)}.width('100%').height('40%').backgroundColor('#eee')}.height('100%').alignItems(VerticalAlign.Top)}
}class Bean {title: stringcolor: ResourceColor
}

        效果如下:

4、构建可滚动的网格布局

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

图9 横向可滚动网格布局

        如果设置的是columnsTemplate,Grid的滚动方向为垂直方向;如果设置的是rowsTemplate,Grid的滚动方向为水平方向。

        如上图所示的横向可滚动网格布局,只要设置rowsTemplate属性的值且不设置columnsTemplate属性,当内容超出Grid组件宽度时,Grid可横向滚动进行内容展示。

@Entry
@Component
struct GridScrollLayoutPage {@State services: Array<string> = ['直播', '进口', '国外', '乡里', '本地', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I']color: ResourceColor[] = [Color.Blue, Color.Green, Color.Orange, Color.Pink, Color.Yellow]build() {Column({ space: 5 }) {Grid() {ForEach(this.services, (service: string, index) => {GridItem() {Text(service).backgroundColor(this.color[index % this.color.length]).fontColor(Color.White).width(72).height(72).fontSize(24).borderRadius(8).textAlign(TextAlign.Center).borderRadius(36)}.width('25%')}, service => service)}.rowsTemplate('1fr 1fr') // 只设置rowsTemplate属性,当内容超出Grid区域时,可水平滚动。.rowsGap(15).columnsGap(12).height('25%')}.borderColor(Color.Pink).borderWidth(1).borderRadius(8).margin(8).backgroundColor("#1067c8ff")}
}

5、实现简单的日历功能

import hilog from '@ohos.hilog';@Entry
@Component
struct CalendarLayoutPage {private scroller: Scroller = new Scroller()private headLabel: string[] = ['一', '二', '三', '四', '五', '六', '日']private curDate: Date;@State curDateStr: string = ''@State dateArray: number[] = []aboutToAppear() {let date = new Date();this.curDate = date;this.changeMonthData()}changeMonthData() {let monthLength = this.getMonthLength(this.curDate);this.dateArray = []for (let index = 0; index < monthLength; index++) {this.dateArray.push(index + 1)}this.curDateStr = `${this.curDate.getFullYear()}-${this.curDate.getMonth() + 1}`}private nextMonth() {this.curDate.setMonth(this.curDate.getMonth() + 1)}private prevMonth() {this.curDate.setMonth(this.curDate.getMonth() - 1)}private getMonthLength(date: Date): number {let copyDate = new Date(date.getFullYear(), date.getMonth() + 1, 0)return copyDate.getDate()}build() {Row() {Column() {Text(this.curDateStr).backgroundColor(Color.Pink).fontColor(Color.White).height(48).padding({ left: 24, right: 24 }).borderRadius(24)Row({ space: 8 }) {ForEach(this.headLabel, (item) => {Text(item).fontSize(14).layoutWeight(1).textAlign(TextAlign.Center).height('40%').borderRadius(26).backgroundColor('#67c8ff')}, item => JSON.stringify(item))}.height(52).backgroundColor('#999').margin({top: 24})Grid() {ForEach(this.dateArray, (item) => {GridItem() {Button(item + "").width(52).height(52).borderWidth(1).borderColor('#67c8ff')}}, item => JSON.stringify(item))}.columnsTemplate('1fr 1fr 1fr 1fr 1fr 1fr 1fr').columnsGap(5).rowsGap(5).margin({ top: 12 }).height('40%')Row({ space: 20 }) {Button('上一页').onClick(() => {this.prevMonth()this.changeMonthData()// this.scroller.scrollPage({//   next: false// })})Button('下一页').onClick(() => {this.nextMonth()this.changeMonthData()// this.scroller.scrollPage({//   next: true// })})}}.width('100%').backgroundColor('#eee').borderRadius(24)}.height('100%').alignItems(VerticalAlign.Top).margin({top: 48, left: 12, right: 12})}
}

        通过 @State dateArray来刷新UI,当点击前一页的时候,当前月份减少1,然后改变dateArray的数据,触发UI更新;点击下一页同理。效果如下:

6、性能优化

        与长列表的处理类似,循环渲染适用于数据量较小的布局场景,当构建具有大量网格项的可滚动网格布局时,推荐使用数据懒加载方式实现按需迭代加载数据,从而提升列表性能。

        关于按需加载优化的具体实现可参考数据懒加载章节中的示例。

        当使用懒加载方式渲染网格时,为了更好的滚动体验,减少滑动时出现白块,Grid组件中也可通过cachedCount属性设置GridItem的预加载数量,只在懒加载LazyForEach中生效。

        设置预加载数量后,会在Grid显示区域前后各缓存cachedCount*列数个GridItem,超出显示和缓存范围的GridItem会被释放。

 

Grid() {LazyForEach(this.dataSource, item => {GridItem() {...}})
}
.cachedCount(3)

说明

cachedCount的增加会增大UI的CPU、内存开销。使用时需要根据实际情况,综合性能和用户体验进行调整。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/336841.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【算法分析与设计】三数之和

题目 给你一个整数数组 nums &#xff0c;判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i ! j、i ! k 且 j ! k &#xff0c;同时还满足 nums[i] nums[j] nums[k] 0 。请 你返回所有和为 0 且不重复的三元组。 注意&#xff1a;答案中不可以包含重复的三元组。 示例…

【LeetCode:49. 字母异位词分组 | 哈希表】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

python 基础 面向对象

类 class Student:name Nonegender Nonenationality Nonenative_place Noneage Nonedef say_hi(self):print(self.name)def dowork(self,work):print(f"{self.name} {work}") student1 Student() student1.name "xxx" student1.gender "男&qu…

数据库开发工具Navicat Premium 15 mac软件特色

Navicat Premium 15 mac版是一款数据库开发工具&#xff0c;Navicat Premium 15 Mac版可以让你以单一程序同時连接到 MySQL、MariaDB、SQL Server、SQLite、Oracle 和 PostgreSQL 数据库。 Navicat Premium mac软件特色 无缝数据迁移 数据传输&#xff0c;数据同步和结构同步…

华为云AI:轻松实现图像识别调用

文章目录 前言一、环境配置关键步骤 二、图像识别实例媒资图像标签名人识别 总结 前言 基于华为云AI服务和java使用SDK实现图像识别&#xff0c;主要以媒资图像标签和名人识别为例。 一、环境配置 Maven&#xff08;没有直接下载华为的SDK包&#xff0c;而是使用Maven安装依赖…

cpolar 内网穿透 使用

cpolar 内网穿透 使用 官网地址&#xff1a;https://www.cpolar.com/ 官网文档&#xff1a;https://www.cpolar.com/blog/cpolar-quick-start-tutorial-centos-series 获取隧道Authtoken&#xff1a;https://dashboard.cpolar.com/auth 步骤 1、先去注册 在这个地方注册&…

[ 机器学习 ] 关于Jupyter Notebook中pytorch模块import失败的问题

0x01、问题描述 在使用WSL搭建Jupyter进行代码测试的时候 发现Miniconda&#xff08;虚拟环境均适用&#xff09;中安装的pytorch在Jupyter里面import失败 但在python解释器的命令模式里可以测试import成功 并且torch.cuda_available()打印True 以前用的是IDEA没怎么用Jup…

模仿Activiti工作流自动建表机制,实现Springboot项目启动后自动创建多表关联的数据库与表的方案

文/朱季谦 熬夜写完&#xff0c;尚有不足&#xff0c;但仍在努力学习与总结中&#xff0c;而您的点赞与关注&#xff0c;是对我最大的鼓励&#xff01; 在一些本地化项目开发当中&#xff0c;存在这样一种需求&#xff0c;即开发完成的项目&#xff0c;在第一次部署启动时&…

Qt之有趣的数字钟

一.效果 基于网络代码修改,支持时、分、秒;支持滑动、翻页和旋转。 二.实现 #include <QtCore> #include <QPainter> #include <QAction> #include <QWidget> #include <QMainWindow> #include <QTimer> #include <QKeyEvent> #…

【一竞技DOTA2】MinD_ContRoL加盟Tundra

1、近日Tundra战队正式官宣MinD_ContRoL加入队伍。他此前效力于Nigma战队,在离队不久后他便做出了加入Tundra战队的选择,借此他也成为了Tundra Esports战队已官宣了的阵容中的第四名选手。 2、Bleed战队正式官宣Mikoto和poloson加入战队。 Mikoto曾效力于东南亚赛区的Talon战队…

深度解析高防产品---高防CDN

高防CDN是一种基于云计算技术的网络安全防御系统&#xff0c;通过在全球范围内部署多个节点&#xff0c;实现对网站内容的加速和保护。其主要作用和功能包括安全防护、加速访问、跨运营商、跨地域的全网覆盖、提高网站的稳定性以及节约成本。高防CDN可以有效地解决不同地区的网…

阿里云实时计算企业级状态存储引擎 Gemini 技术解读

本文整理自阿里云 Flink 存储引擎团队李晋忠&#xff0c;兰兆千&#xff0c;梅源关于阿里云实时计算企业级状态存储引擎 Gemini 的研究&#xff0c;内容主要分为以下五部分&#xff1a; 流计算状态访问的痛点企业级状态存储引擎GeminiGemini 性能评测&线上表现结语参考 一、…