第二篇:使用网格Grid与列表list
背景:鸿蒙0基础探索。
愿景:本系列旨在用简单直接的方式从让鸿蒙app先从0-1。
理由:有产出更有继续下去的动力。
记录:每篇文章保留下探索的过程。
PS: 不教写代码,不封装,业务代码能力因人而异。(关键我不会啊😁)
第三篇:接入第三方库,使用Axios网络请求,PullToRefresh上拉刷新下拉加载
书接上回。我们有了tabs和导航栏之后,立马就开始探索“列表”在鸿蒙中是用什么。为什么是探索这个,因为它实在是太被常用了,一个APP大部分的页面都多少离不开列表。(你要相信像Button, label,if,不会成为探索路上的拦路虎,我学会了列表,那些东西很轻松就能学会)。
我们在上一篇典型应用场景中同时也发现了网格,轮播等。自然就发现,在鸿蒙中可以通过Grid和List实现"列表"的功能
探索中遇到的痛点
痛点一:编译器参数提示
参数提示,不知道有哪些参数能写。用习惯Xcode的,参数会跟着方法一起出来,还不适应每写一个方法都要跳进定义里找,有的参数套几层,跳来跳去,给自己跳晕了,不知道自己要干嘛了。(不过咨询了一下,安卓开发好像一直是这样,那估计这一点编译器支持不了,去习惯它,如果有请大佬告诉我下)。
痛点二:编译器内⌘ + 左键:查看参数定义(windows快捷键好像control + 左键),后面发了新的查看方式,就好用很多 ⌘ + 7
看定义详情,注释太长,分了各个api的版本,真的看不清楚一个实例到底有哪些功能。 对阅读源码接口定义真的很不友好。
举个例子:点击了Grid() {}的定义,点进来看到的全是注释。这样的例子充斥着整个文档。
咨询了同事,Android Studio中是否有查看页面的方法参数定义的一个快捷浏览。以此来推测DevEco-Studio是否有。于是乎找到了这个Navigate -> File Structure(文件结构),我们还是拿Grid的定义举例。, 看后面不错的新的方式
下面这个是我结合定义猜的,应该大差不差。如果单词拼写错误,请忽略,意会了就行
类相关:
- C: 抽象类:class。
- I: 实体类:Interface
- E: 枚举:Enum
方法相关:
- m: 修改器/装饰器: modifier
- p: 属性/参数: param/property
- 黄色f: 枚举的值,词汇量有限,不知道哪个英文词
- 红色f:方法:function
通过这个,来查看定义绝对比查看api文档快上很多,而且熟练以后更多是在编译上查看,不会经常打开网页去查。
ps:这个功能是有了,就是吧:这个交互上实在不好用。 希望编译器团队优化优化。
顺便再提点建议,希望能鼠标悬停在类上时,弹出的提示里,能直接给上几段示例代码。参考SwiftUI。
痛点二替代方案: 我们来看看后来发现的 ⌘ + 7
我们后来发现Structrue。这东西,然后把他固定在了右边。里面还有一个view Mode的配置。可以调整悬浮窗等,看个人习惯,去试试吧。
以m: modifier为例,有些英文一眼就能看出是干什么的,比如minCout,maxCount,rowsGap,columnsGap.
Grid() {
}
// 那我就可以这样设置行间距
.rowsGap(10)
相关概念
去看Grid的相关资料时,自然而然引出了。Grid,GirdItem,ForEach(支持渲染控制类型之一,推荐先了解这种,够常用。 LazyForEach和Repeat看个人状态,我没看,等以后遇到了看也来得及)。
- 网格Grid:官方文档链接,这个文档示例代码都半截子,没办法还原示例。 官方api文档。
- ForEach: 文档有这样一句话:Grid仅支持GridItem子组件,支持渲染控制类型(if/else、ForEach、LazyForEach和Repeat)。
- GridItem:是Grid唯一支持的小伙伴,也就是说网格里每一个组件一定是GridItem,至于GridItem里可以包装什么就随意。如果你也是iOS开发,理解成UITableView 和 UITableViewCell的关系就挺好理解。
列表List。 官方文档链接
网格布局Grid 官方文档链接
文档看的还是一头包,主要是文档示例代码都半截子,没办法还原示例。
文档中穿插的额几个说明,不用急着理解,记录一下。
说明 当Grid组件设置了rowsTemplate或columnsTemplate时,Grid的layoutDirection、maxCount、minCount、cellLength属性不生效,属性说明可参考Grid-属性。
说明 Grid的子组件必须是GridItem组件。
说明 Grid组件根据行列数量与占比属性的设置,可以分为三种布局情况:
- rowsTemplate,columnsTemplate同时设置:Grid只展示固定行列数的元素,其余元素不展示,且Grid不可滚动。(推荐使用该种布局方式)
- 只设置rowsTemplate:可横向滚动
- 只设置columnsTemplate:可纵向滚动。
- 都不设置:元素在布局方向上排布,其行列数由布局方向、单个网格的宽高等多个属性共同决定。超出行列容纳范围的元素不展示,且Grid不可滚动。
自定义GridItem的所占行列数
看文档的时候肯定有很多困惑,比如要自定义某个item的宽高,设置设置子组件所占行列数。
看不懂,困惑的点:
1.我怎么知道Grid能设置GridLayoutOptions这个。
2.Grid里还有什么参数可以设置。
3.GridLayoutOptions这里面又有哪些能设置。
我们在痛点2中提到的方法就派上用场了。
1.Grid对象只提供了一个方法初始化,有且只有两个?(可选)参数。所以我们可以用Grid() {}这样的写法初始化。
2.我再看看GridLayoutOptions的几个参数,3个可选,1个regularSize是必填, 类型为一个number[]数组,有两个值。所以可以这样写:
// 所以初始化GridLayoutOptions至少要有regularSize参数。
layoutOptions: GridLayoutOptions = {
// 常规item的宽高
regularSize: [1, 1],
}
3.我们在图上看看,文档上要我们使用的onGetRectByIndex是如何定义的。给了一个index的参数,类型是number. 需要我们返回一个numbers[]数组,有4个值。于是乎可以这样写一定不会报错:(下一步研究这几个值代表是什么意思)
layoutOptions: GridLayoutOptions = {
// 常规item的宽高
regularSize: [1, 1],
// 设置rect通过下标。
onGetRectByIndex: (index: number) => {
return [0, 0, 1, 1]
}
}
4.先通过名字onGetRectByIndex大概猜一下:“读取,位置信息,通过,下标”。所以里面取值大概是是能表示位置信息的。这时候我们就点进这个参数看注释。[rowStart, columnStart, rowSpan, columnSpan]
如果还有困惑,就再去官方文档上再看看中文解释,相信一定会有收获。[起始行, 起始列, 要占几行, 要占几列]。 如果你也是iOS的话,理解为frame(x:, y:, width: , height:)就挺好理解。这样我们就可以结合index参数,为某一些item设置特殊位置。
据我观察:使用GridLayoutOptions配置时Grid时,需要指明. 行模板:rowsTemplate, 列模板columnsTemplate。相当于知道了有 几行几列,才好设置特殊的大小GridItem。
比如我这个4行4列的grid:注意看图上数据对应排列的位置。
提问:如果我把下标为4的格子,宽度改占3列。 明显4和6所占的位置会打架,那会发生什么。
修改
else if (index == 4) {
return [2, 0, 1, 3]
}
由此我猜测应该是这样:,onGetRectByIndex执行的顺序会从上往下开始排,谁写前面先排谁。 当我index == 1,index == 4排完之后。 遇到了index == 6,发现4和6的位置是冲突的。那6就会去走常规布局
横向滑动
需要设置 行模板rowsTemplate,最好也指定个width。
点击展开代码
@Entry
@Component
export struct RankPage {
// 数据源,
modelArray: string[] = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]
build() {
Grid() {
ForEach(this.modelArray, (item: string, index) => {
// 在这里面构建每一个item的布局。 GridItem相当于iOS中: UITableViewCell / UIColletionViewCell
GridItem() {
Text(item)
.backgroundColor(Color.White)
}
.width('25%')
.backgroundColor(Color.Pink)
})
}
// 比如我指定了高度,不指定就默认充满父视图。
.height(300)
// 行的模板
.rowsTemplate('1fr 1fr')
// 设置列间距
.columnsGap(10)
// 设置行间距
.rowsGap(25)
.backgroundColor(Color.Yellow)
}
}
纵向滑动
需要设置 列模板columnsTemplate,最好也指定个height。
点击展开代码
@Entry
@Component
export struct RankPage {
// 数据源,
modelArray: string[] = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]
build() {
Grid() {
ForEach(this.modelArray, (item: string, index) => {
// 在这里面构建每一个item的布局。 GridItem相当于iOS中: UITableViewCell / UIColletionViewCell
GridItem() {
Text(item)
.backgroundColor(Color.White)
}
.height('25%')
.backgroundColor(Color.Pink)
})
}
// 比如我指定了高度,不指定就默认充满父视图。
.height(300)
// 列的模板
.columnsTemplate('1fr 1fr')
// 设置列间距
.columnsGap(10)
// 设置行间距
.rowsGap(25)
.backgroundColor(Color.Yellow)
}
}
计算器布局示例
补充了一下文档中的示例代码。数据源modelArray可以是自定义了对象,为了方便演示简化了。
需要注意的是图上,第一块粉色区域,占1行4列,配合rowsTemplate第一行的2fr,代表占了2份高度。 fr是用 空格 分隔的,不是逗号。
点击展开代码
@Entry
@Component
export struct RankPage {
// 数据源,
modelArray: string[] = [
"",
"CE", "C", "/", "X",
"7", "8", "9", "-",
"4", "5", "6", "+",
"1", "2", "3", "=",
"0", "."]
layoutOptions: GridLayoutOptions = {
regularSize: [1, 1],
onGetRectByIndex: (index: number) => {
if (index == 0) { // 第一个空字符串相当于结果展示位 占了1行4列。(第一行的高度是2fr)
return [0, 0, 1, 4]
} else if (index == 16) { // "="号,占2行一列
return [4, 3, 2, 1]
} else if (index == 17) { // "0"号,占1行两列
return [5, 0, 1, 2]
}
return [0, 0, 1 , 1]
}
}
build() {
Grid(undefined ,this.layoutOptions) {
ForEach(this.modelArray, (item: string, index) => {
// 在这里面构建每一个item的布局。 GridItem相当于iOS中: UITableViewCell / UIColletionViewCell
GridItem() {
Text(item)
.backgroundColor(Color.White)
}
.backgroundColor(Color.Pink)
})
}
// 比如我指定了高度,不指定就默认充满父视图。
.height(400)
// 行的模板
.rowsTemplate('2fr 1fr 1fr 1fr 1fr 1fr')
.columnsTemplate('1fr 1fr 1fr 1fr')
// 设置列间距
.columnsGap(10)
// 设置行间距
.rowsGap(10)
.backgroundColor(Color.Yellow)
}
}
列表布局list 官方文档链接
关于list的探索,就交给各位自己了,木有问题。
这一期做的效果
决定结合上一期我们学到的tabs标签栏, 结合Grid,顺便加上轮播Swiper,做一个宝可梦列表的练手的效果。
看看效果
Demo代码
最后附上demo地址,第二篇内容,把用得上几个文件拷进自己新建的工程就行。别忘了module.json5里加 "routerMap": "$profile:route_map",。 还有route_map.json文件别忘了。