WaterFlow和Grid布局非常的类似
瀑布流容器,由“行”和“列”分割的单元格所组成,通过容器自身的排列规则,将不同大小的“项目”自上而下,如瀑布般紧密布局。
说明
该组件从API Version 9 开始支持。后续版本如有新增内容,则采用上角标单独标记该内容的起始版本。
子组件
包含FlowItem子组件。
说明
WaterFlow子组件的visibility属性设置为None时不显示,但该子组件周围的columnsGap、rowsGap、margin仍会生效。
- 使用
接口
WaterFlow(options?: WaterFlowOptions)
参数:
参数名 | 参数类型 | 必填 | 参数描述 |
options | 是 | 瀑布流组件参数。 |
WaterFlowOptions对象说明
参数名 | 参数类型 | 必填 | 参数描述 |
footer | 自定义的Builde函数 | 否 | 设置WaterFlow尾部组件。 |
scroller 滚动条组件 | 否 | 可滚动组件的控制器,与可滚动组件绑定。 |
CustomBuilder ?
意味着可以传入一个用Builder修饰符修饰的函数-来自定义结构的部分
Custom后面是啥,就意味着可以传入用啥修饰的函数
逻辑分析
我们的WaterFlow组件里面只能放置我们的FlowItem组件,然后我们的FlowItem组件里面再放置我们的想要的内容
如果我们想要吧它排列成多列的话跟我们的Grid布局的设置的方式是一样的,同样的是用我们的columnsTemplate,这个熟悉来设置,比如说我先要吧它设置成两列同宽的那么就如下设置方式
.columnsTemplate("1fr 1fr")
如果我们想要设置它的一个间隙的话,那么跟Grid里面也是一样的,可以用.columnsGap的方式进行设置列边距
比如我想要吧它们的列间距设置成10 .columnsGap(10)
行间距,我们可以用rowsGap 来设置它的一个行之间的间距
然后这个组件也是可以用内外边距的
里面的属性 | 作用 | 使用方法 |
.columnsTemplate | 设置它们之间的一个排列方式为几行 | columnsTemplate(这里面写上几个1fr就是几行) fr前面是几就代表占用几份 |
.columnsGap | 每一列之间的一个间距 | .columnsGap(这里传它们之间的间距的一个距离) |
.rowsGap | 每一行之间的一个间距 | .rowsGap(这里传它们之间的间距的一个距离) |
然后它们之间是怎么设置不同大小的一个图片布局呢
下面我们来做一个小的案例
我们把下面的这4张图片设置成不同的大小
我们可以再图片下面之间设置它的一个属性,比如宽高
@Entry
@Component
struct WaterFlowCase {
build() {
//先定义这个容器布局
WaterFlow(){
//然后里面放我们的一个内容,也就是我们的组件,然后这里面只支持放FlowItem
FlowItem(){
Image($r('app.media.1'))
.height(300)
}
FlowItem(){
Image($r('app.media.2'))
.height(200)
}
FlowItem(){
Image($r('app.media.3'))
.height(300)
}
FlowItem(){
Image($r('app.media.4'))
.height(200)
}
}
.columnsTemplate("1fr 1fr")
// 我们再这里再设置它们之间行的一个间距为20
.rowsGap(20)
//然后再设置它的一个内边距
.padding(20)
//设置它的一个列间距
.columnsGap(20)
}
}
上面的这些是它的一个基本的使用,下面上点压力,再做一个案例
我们把之前做的那个商城的那个案例优化一下做出瀑布流的
我们先把之前做的那个商城的那个页面中的数据拿出来
我们拿数据的时候要注意一点就是这个数据有一个类型
我们之前写过这个类型的一个类和这个类型的接口,但是如果我们把之前写的这个类型再写一遍,还是这个名字的话,那么我们运行的时候是会报错的,因为鸿蒙当中是不允许两个函数的名字是相同的,既然我们之前写过这个类,那么我们直接导入即可
引进来之后,接下来我们就该要实现我们的布局了
我们先看看能不能正常的把数据进行一个展示
我们发现数据是可以正常的进行一个展示的,接下来我们就可以来完成它的样式了
我们想要的是把它们左右之间有一个错开的一个感觉,就需要下面的方法了
首先我们先分析一下业务,就是我们要做到那种瀑布流的一个排列布局的话,那么就需要我们左右两边错开
然后我们用循环的话怎么设置它们左右两边错开的一个方法呢
我们可以先计算我们的单数,或者双数,计算出来我们的单数或者双数之后,我们可以利用这个单数或者是双数的一个差距来定义不同的高度
我们先让它对2取余,然后可以除尽的就是偶数,给它设置宽度为220,除不尽的我们就给它设置为180
所以接下来我们就这样写
为什么上面的那个取余的没有写大于0或者小于0这个呢,是因为有隐式转化的
什么是隐式转化呢,就是比如说一个数是0,那么会自动的隐士转化成false的,如果大于0,那么会自动的转换成true
然后我们完善过之后的代码和案例如下
import { GoodItem} from '../04/models'
@Entry
@Component
struct WaterFlowGoodCase {
//先创建数据源
@State list: GoodItem[] = [
{
"id": 1,
"goods_name": "班俏BANQIAO超火ins潮卫衣女士2020秋季新款韩版宽松慵懒风薄款外套带帽上衣",
"goods_img": "assets/1.webp",
"goods_price": 108,
"goods_count": 1,
},
{
"id": 2,
"goods_name": "嘉叶希连帽卫衣女春秋薄款2020新款宽松bf韩版字母印花中长款外套ins潮",
"goods_img": "assets/2.webp",
"goods_price": 129,
"goods_count": 1,
},
{
"id": 3,
"goods_name": "思蜜怡2020休闲运动套装女春秋季新款时尚大码宽松长袖卫衣两件套",
"goods_img": "assets/3.webp",
"goods_price": 198,
"goods_count": 1,
},
{
"id": 4,
"goods_name": "思蜜怡卫衣女加绒加厚2020秋冬装新款韩版宽松上衣连帽中长款外套",
"goods_img": "assets/4.webp",
"goods_price": 99,
"goods_count": 1,
},
{
"id": 5,
"goods_name": "幂凝早秋季卫衣女春秋装韩版宽松中长款假两件上衣薄款ins盐系外套潮",
"goods_img": "assets/5.webp",
"goods_price": 156,
"goods_count": 1,
},
{
"id": 6,
"goods_name": "ME&CITY女装冬季新款针织抽绳休闲连帽卫衣女",
"goods_img": "assets/6.webp",
"goods_price": 142.8,
"goods_count": 1,
},
{
"id": 7,
"goods_name": "幂凝假两件女士卫衣秋冬女装2020年新款韩版宽松春秋季薄款ins潮外套",
"goods_img": "assets/7.webp",
"goods_price": 219,
"goods_count": 2,
},
{
"id": 8,
"goods_name": "依魅人2020休闲运动衣套装女秋季新款秋季韩版宽松卫衣 时尚两件套",
"goods_img": "assets/8.webp",
"goods_price": 178,
"goods_count": 1,
},
{
"id": 9,
"goods_name": "芷臻(zhizhen)加厚卫衣2020春秋季女长袖韩版宽松短款加绒春秋装连帽开衫外套冬",
"goods_img": "assets/9.webp",
"goods_price": 128,
"goods_count": 1,
},
{
"id": 10,
"goods_name": "Semir森马卫衣女冬装2019新款可爱甜美大撞色小清新连帽薄绒女士套头衫",
"goods_img": "assets/10.webp",
"goods_price": 153,
"goods_count": 1,
}
]
build() {
WaterFlow(){
//因为数据太多,所以我们这里用循环来把它们全部列出
ForEach(this.list,(item:GoodItem,ind:number)=>{
//因为它的里面只能放置FlowItem组件
FlowItem(){
Column({space:10}){
Image(item.goods_img)
//对二取余,如果取尽了,那么就是双数,如果取不尽,那么就是单数
//然后我们根据单双来设置它的一个高度,这样子就实现了我们的瀑布流错峰排列
.height(ind % 2 ? 220 :180)
.borderRadius(4)//设置它的一个圆角
Text(item.goods_name)
.fontSize(14)
.lineHeight(22)//行高
}
}
})
}
.columnsTemplate('1fr 1fr')
.padding(20) //设置它的内边距
.columnsGap(20) //.columnsGap 每一列之间的一个间距
.rowsGap(20) //.rowsGap 每一行之间的一个间距
}
}
我们写完之后,再顺便学一下builder,把它抽屉成内容,然后实现轻量级的UI复用
加上builder进行完善
加上builder之后进行一个优化
优化后的代码如下
import { GoodItem} from '../04/models'
@Entry
@Component
struct WaterFlowGoodCase {
//先创建数据源
@State list: GoodItem[] = [
{
"id": 1,
"goods_name": "班俏BANQIAO超火ins潮卫衣女士2020秋季新款韩版宽松慵懒风薄款外套带帽上衣",
"goods_img": "assets/1.webp",
"goods_price": 108,
"goods_count": 1,
},
{
"id": 2,
"goods_name": "嘉叶希连帽卫衣女春秋薄款2020新款宽松bf韩版字母印花中长款外套ins潮",
"goods_img": "assets/2.webp",
"goods_price": 129,
"goods_count": 1,
},
{
"id": 3,
"goods_name": "思蜜怡2020休闲运动套装女春秋季新款时尚大码宽松长袖卫衣两件套",
"goods_img": "assets/3.webp",
"goods_price": 198,
"goods_count": 1,
},
{
"id": 4,
"goods_name": "思蜜怡卫衣女加绒加厚2020秋冬装新款韩版宽松上衣连帽中长款外套",
"goods_img": "assets/4.webp",
"goods_price": 99,
"goods_count": 1,
},
{
"id": 5,
"goods_name": "幂凝早秋季卫衣女春秋装韩版宽松中长款假两件上衣薄款ins盐系外套潮",
"goods_img": "assets/5.webp",
"goods_price": 156,
"goods_count": 1,
},
{
"id": 6,
"goods_name": "ME&CITY女装冬季新款针织抽绳休闲连帽卫衣女",
"goods_img": "assets/6.webp",
"goods_price": 142.8,
"goods_count": 1,
},
{
"id": 7,
"goods_name": "幂凝假两件女士卫衣秋冬女装2020年新款韩版宽松春秋季薄款ins潮外套",
"goods_img": "assets/7.webp",
"goods_price": 219,
"goods_count": 2,
},
{
"id": 8,
"goods_name": "依魅人2020休闲运动衣套装女秋季新款秋季韩版宽松卫衣 时尚两件套",
"goods_img": "assets/8.webp",
"goods_price": 178,
"goods_count": 1,
},
{
"id": 9,
"goods_name": "芷臻(zhizhen)加厚卫衣2020春秋季女长袖韩版宽松短款加绒春秋装连帽开衫外套冬",
"goods_img": "assets/9.webp",
"goods_price": 128,
"goods_count": 1,
},
{
"id": 10,
"goods_name": "Semir森马卫衣女冬装2019新款可爱甜美大撞色小清新连帽薄绒女士套头衫",
"goods_img": "assets/10.webp",
"goods_price": 153,
"goods_count": 1,
}
]
@Builder
getFlowItem(item:GoodItem,ind:number){
//再这里写我们的ArkTS
Column({space:10}) {
Image(item.goods_img)//对二取余,如果取尽了,那么就是双数,如果取不尽,那么就是单数
//然后我们根据单双来设置它的一个高度,这样子就实现了我们的瀑布流错峰排列
.height(ind % 2 ? 220 : 180)
.borderRadius(4) //设置它的一个圆角
Text(item.goods_name)
.fontSize(14)
.lineHeight(22) //行高
}
}
build() {
WaterFlow(){
//因为数据太多,所以我们这里用循环来把它们全部列出
ForEach(this.list,(item:GoodItem,ind:number)=>{
//因为它的里面只能放置FlowItem组件
FlowItem(){
this.getFlowItem(item,ind) //我们这里把参数传进来
}
})
}
.columnsTemplate('1fr 1fr')
.padding(20) //设置它的内边距
.columnsGap(20) //.columnsGap 每一列之间的一个间距
.rowsGap(20) //.rowsGap 每一行之间的一个间距
}
}
waterflow开关阀门的应用
我们这个写完之后再做一个优化,就是我们正常的应用划到底部的时候,因该是要出现一个加载下一页的
我们这里先创建一个buder修饰的一个函数,然后把要再底部展示的内容写到这个里面
写完之后,我们再通过footer这个参数的值,把刚刚定义好的函数传进去
我们先来测试一下看看能不能正常的进行展示
我们经过测试是发现我们再这个代码当中写的内容是可以再最下面展示的,然后我们可以利用这个特性再做一个美化
官方的进度条:LoadingProgress
我们再这里再加一个进度条
进度条的话,官方写好的有一个组件我们直接调用即可
LoadingProgress 就是这个组件,我们直接调用即可
加载下一页
我们上面写完之后,我们发现我们光有这个进度条不是特别的行,按理来说接下来应该还要加载下一页
那么我们怎么来让它加载下一页呢
再这里提一下,所有的事件,除了手势事件以外全部都是on开头的
因为我们的这个WaterFlow是支持滚动的,那么它就具备一个滚动的事件
然后我们滚动到底部的时候会触发一个如下的一个事件
onReachEnd【昂瑞吃恩的:滚动到底部】
同理,滚动到顶部就是
onReachStart【昂瑞吃死大特】
我们先写一个当他滑动到底部的时候让它触发一个弹窗,看看能不能把这个弹窗弹出来
我们可以看到我们是可以弹出来这样一个东西的
有了这个逻辑的话,我们就可以进行加载下一页的数据
但是我们写到这一步的时候会遇到一个问题,这个问题就是,当我们加载到底部的时候,我们再往下面滑动的时候,还会再触发一次这样的一个事件,就会造成多次触发的一个逻辑,也就是多次点击,这样会造成我们的数据再没加载的时候又加载了一遍,让它加载两边的一个情况
所以我们遇到这种频发收到请求的情况的话,我们就需要加上一个阀门,让它执行完上一次然后再去触发下一次的一个逻辑
那我们应该怎么做的呢
这里就需要用上阀门控制了,什么是阀门控制呢,也就是只要我把这个阀门开开了,任何事都不要打扰我。只有我把这个阀门关掉了,这样才可以,所以我们就需要用到一个变量来标记,这个变量就是这个控制阀门
这个变量一定是全局的,然后是用@State修饰的,因为这个可能影像到其他内容的渲染
然后我们加载的使用应该是往list里面添加我们的一个数据,然后我们怎么往list里面添加我们的一个数据呢
我们先定义一个函数,然后往这个函数里面放一个定时器,用来模拟这个网络请求,让它1秒之后往里面加数据
这里穿插一个知识点
往列表的底部添加内容的话
我们先定义到它的一个列表,用this的方法,拿到这个列表之后,用列表的.push的方法去进行添加
比如说我们有一个叫list的一个列表我们想要往里面添加数据的话,那么就是
this.list.push(这里面写要添加的内容)
这个写完之后我们发现了一个大的一个bug,就是我们之前写的那个loading是一直等于false的,所以我们再执行这个函数的时候是应该把这个函数把它变成true,当它刷新完成之后再把它关掉
所以接下来我们这样写
这样我们的这个效果就已经做好了,但是还是有一个小小的一个bug,就是当我们往下面一直刷的时候,我们往里面加的内容会越来越多,因为第二次添加的时候,我们是把第一次添加的时候的内容再添加一次,这样我们就添加两份了,所以我们这里要稍微的改一下
我们添加数据的时候可以不用把它们全部都添加完毕,我们可以只要这个数组里面的前10条数据,然后只添加这10条
插播一个知识点
我们再数组当中要截取一段数据的话可以用slice方法来截取数据,
比如说我想截取一个数组的前10条数据,那么操作如下
this.list.slice(0,9) //这里的list是一个数组的意思
这个是es6的一个写法,除了这个es6里面的一个写法以外还有一个写法就是es5里面的写法
this.list = this.list.concat(...this.list.slice(0,9)) //es5的写法
这个意思是让它把这个列表里面的数据链接到这个列表当中,然后从新赋值到这个列表里面
所以我们更改之后是一个代码如下
右边的是它的一个效果图
它的实现的代码如下
import { GoodItem} from '../04/models'
import { promptAction } from '@kit.ArkUI'
@Entry
@Component
struct WaterFlowGoodCase {
//先创建数据源
@State list: GoodItem[] = [
{
"id": 1,
"goods_name": "班俏BANQIAO超火ins潮卫衣女士2020秋季新款韩版宽松慵懒风薄款外套带帽上衣",
"goods_img": "assets/1.webp",
"goods_price": 108,
"goods_count": 1,
},
{
"id": 2,
"goods_name": "嘉叶希连帽卫衣女春秋薄款2020新款宽松bf韩版字母印花中长款外套ins潮",
"goods_img": "assets/2.webp",
"goods_price": 129,
"goods_count": 1,
},
{
"id": 3,
"goods_name": "思蜜怡2020休闲运动套装女春秋季新款时尚大码宽松长袖卫衣两件套",
"goods_img": "assets/3.webp",
"goods_price": 198,
"goods_count": 1,
},
{
"id": 4,
"goods_name": "思蜜怡卫衣女加绒加厚2020秋冬装新款韩版宽松上衣连帽中长款外套",
"goods_img": "assets/4.webp",
"goods_price": 99,
"goods_count": 1,
},
{
"id": 5,
"goods_name": "幂凝早秋季卫衣女春秋装韩版宽松中长款假两件上衣薄款ins盐系外套潮",
"goods_img": "assets/5.webp",
"goods_price": 156,
"goods_count": 1,
},
{
"id": 6,
"goods_name": "ME&CITY女装冬季新款针织抽绳休闲连帽卫衣女",
"goods_img": "assets/6.webp",
"goods_price": 142.8,
"goods_count": 1,
},
{
"id": 7,
"goods_name": "幂凝假两件女士卫衣秋冬女装2020年新款韩版宽松春秋季薄款ins潮外套",
"goods_img": "assets/7.webp",
"goods_price": 219,
"goods_count": 2,
},
{
"id": 8,
"goods_name": "依魅人2020休闲运动衣套装女秋季新款秋季韩版宽松卫衣 时尚两件套",
"goods_img": "assets/8.webp",
"goods_price": 178,
"goods_count": 1,
},
{
"id": 9,
"goods_name": "芷臻(zhizhen)加厚卫衣2020春秋季女长袖韩版宽松短款加绒春秋装连帽开衫外套冬",
"goods_img": "assets/9.webp",
"goods_price": 128,
"goods_count": 1,
},
{
"id": 10,
"goods_name": "Semir森马卫衣女冬装2019新款可爱甜美大撞色小清新连帽薄绒女士套头衫",
"goods_img": "assets/10.webp",
"goods_price": 153,
"goods_count": 1,
}
]
@Builder
getFlowItem(item:GoodItem,ind:number){
//再这里写我们的ArkTS
Column({space:10}) {
Image(item.goods_img)//对二取余,如果取尽了,那么就是双数,如果取不尽,那么就是单数
//然后我们根据单双来设置它的一个高度,这样子就实现了我们的瀑布流错峰排列
.height(ind % 2 ? 220 : 180)
.borderRadius(4) //设置它的一个圆角
Text(item.goods_name)
.fontSize(14)
.lineHeight(22) //行高
}
}
@Builder
getFooter(){
Row({space:4}){
Text("数据加载")
//这里再放一个进度条
LoadingProgress()//官方写好的一个进度条,我们再这里可以直接调用的
//我们调用完成之后我们可以再给它设置一个宽高,因为如果不设置的话,这个组件有一点点的大
.width(24)
.height(24)
}
.justifyContent(FlexAlign.Center)//设置它的一个对齐方式为居中对齐
.width("100%")
.height(60)
}
@State //再这里做一个阀门的一个控制
loading:boolean = false
//加载更多的数据
loadMore(){
//模拟发送网络请求
setTimeout(()=>{
//拿到数据,然后追加
this.list.push(...this.list.slice(0,9)) //es6的写法
// this.list = this.list.concat(...this.list.slice(0,9)) //es5的写法
//数据加载完成之后再把阀门关闭
this.loading = false
},1000)
}
build() {
//可以支持滚动
WaterFlow({footer:this.getFooter}){
//因为数据太多,所以我们这里用循环来把它们全部列出
ForEach(this.list,(item:GoodItem,ind:number)=>{
//因为它的里面只能放置FlowItem组件
FlowItem(){
this.getFlowItem(item,ind) //我们这里把参数传进来
}
})
}
.onReachEnd(()=>{
// promptAction.showToast({message:'滚动到底部'})
//频繁收到请求,需要执行完上一次再说
//这里要做阀门控制
if (!this.loading) { //如果它没有加载的情况下,那么它才能加载
this.loading = true //把这个阀门打开,让它无法再次触发
this.loadMore()
}
})
.columnsTemplate('1fr 1fr')
.padding(20) //设置它的内边距
.columnsGap(20) //.columnsGap 每一列之间的一个间距
.rowsGap(20) //.rowsGap 每一行之间的一个间距
}
}