在HarmonyOS应用开发中,瀑布流布局因其灵活性和美观性而广受欢迎。HarmonyOS NEXT 提供了强大的 WaterFlow
组件,可以帮助开发者轻松实现瀑布流布局,并支持多种自定义布局和性能优化特性。本文将通过两个具体场景,详细介绍如何使用 WaterFlow
组件实现页面滑动加载和吸顶效果。
场景一:瀑布流页面多列混排的布局场景
场景描述
在一个瀑布流页面中,不同区域的布局可能有所不同,例如前10个item在2列内布局,中间5个item在1列内撑满宽度布局,后10个item在3列内布局。
实现思路
- 计算FlowItem宽/高:动态计算每个item的宽高,确保布局的多样性。
- 设置FlowItem的宽/高数组:预设每个item的宽高,以便在布局时使用。
- 配置分组信息:通过
sections
配置分组信息,支持不同列数的布局。 - 懒加载:在即将触底时提前加载更多数据,确保性能。
核心代码
@Entry
@Component
struct WaterFlowLayout {
@State dataSource: Array<number> = [];
@State sections: WaterFlowSections = new WaterFlowSections();
@State itemWidthArray: Array<number> = [];
@State itemHeightArray: Array<number> = [];
@State dataCount: number = 100;
@State maxSize: number = 200;
@State minSize: number = 100;
@State scroller: Scroller = new Scroller();
getSize() {
let ret = Math.floor(Math.random() * this.maxSize);
return (ret > this.minSize ? ret : this.minSize);
}
setItemSizeArray() {
for (let i = 0; i < 100; i++) {
this.itemWidthArray.push(this.getSize());
this.itemHeightArray.push(this.getSize());
}
}
aboutToAppear() {
this.setItemSizeArray();
let sectionOptions: SectionOptions[] = [];
let count = 0;
let oneOrTwo = 0;
while (count < this.dataCount) {
if (oneOrTwo++ % 2 == 0) {
sectionOptions.push({
itemsCount: 4,
crossCount: 1,
columnsGap: 5,
rowsGap: 10,
margin: { top: 10, left: 5, bottom: 10, right: 5 },
onGetItemMainSizeByIndex: (index: number) => 150
});
count += 4;
} else {
sectionOptions.push({
itemsCount: 20,
crossCount: 2,
onGetItemMainSizeByIndex: (index: number) => this.itemHeightArray[index % 100]
});
count += 20;
}
}
this.sections.splice(-1, 0, sectionOptions);
}
build() {
Column() {
WaterFlow({ scroller: this.scroller, sections: this.sections })
.height('100%')
.width('100%')
.onScrollIndex((first: number, last: number) => {
if (last + 20 >= this.dataSource.length) {
for (let i = 0; i < 100; i++) {
this.dataSource.push(this.dataSource.length);
}
let newSection: SectionOptions = {
itemsCount: 100,
crossCount: 2,
onGetItemMainSizeByIndex: (index: number) => this.itemHeightArray[index % 100]
};
this.sections.push(newSection);
}
})
.children(LazyForEach(this.dataSource, (item: number) => {
FlowItem() {
Column() {
Image(`./Image/${item % 10}.png`)
.objectFit(ImageFit.Cover)
.width('90%')
.height(100)
.layoutWeight(1)
.margin(5);
Text("必吃榜").fontSize(12).height('16');
}
}
.width('100%')
.height(this.itemHeightArray[item % 100])
.backgroundColor(Color.White);
}, (item: string) => item));
}
}
}
场景二:瀑布流页面中某一个Item固定展示在某一个位置
场景描述
在瀑布流页面中,某个Item需要固定展示在某个位置,当页面滑动时,该Item在到达吸顶位置后保持不动,其他内容继续滑动。
实现思路
- 预留吸顶部分位置:在第一个分组中剔除特定的Item,为吸顶部分留出位置。
- 数据渲染:在数据渲染时剔除特定的Item,确保吸顶部分不被覆盖。
- 监听滚动事件:通过
onWillScroll
回调监听滚动事件,根据滚动偏移量调整吸顶部分的位置。
核心代码
@Entry
@Component
struct StickyItemLayout {
@State dataSource: Array<number> = [];
@State sections: WaterFlowSections = new WaterFlowSections();
@State itemWidthArray: Array<number> = [];
@State itemHeightArray: Array<number> = [];
@State dataCount: number = 100;
@State maxSize: number = 200;
@State minSize: number = 100;
@State scroller: Scroller = new Scroller();
@State scrollOffset: number = 0;
getSize() {
let ret = Math.floor(Math.random() * this.maxSize);
return (ret > this.minSize ? ret : this.minSize);
}
setItemSizeArray() {
for (let i = 0; i < 100; i++) {
this.itemWidthArray.push(this.getSize());
this.itemHeightArray.push(this.getSize());
}
}
aboutToAppear() {
this.setItemSizeArray();
let sectionOptions: SectionOptions[] = [];
sectionOptions.push({
itemsCount: 3,
crossCount: 1,
columnsGap: 5,
rowsGap: 10,
margin: { top: 10, left: 5, bottom: 10, right: 5 },
onGetItemMainSizeByIndex: (index: number) => {
if (index == 1) {
return 100; // 剔除Item=1,为吸顶部分留出位置
} else {
return 200;
}
}
});
this.sections.splice(-1, 0, sectionOptions);
}
build() {
Column() {
WaterFlow({ scroller: this.scroller, sections: this.sections })
.height('100%')
.width('100%')
.onScrollIndex((first: number, last: number) => {
if (last + 20 >= this.dataSource.length) {
for (let i = 0; i < 100; i++) {
this.dataSource.push(this.dataSource.length);
}
let newSection: SectionOptions = {
itemsCount: 100,
crossCount: 2,
onGetItemMainSizeByIndex: (index: number) => this.itemHeightArray[index % 100]
};
this.sections.push(newSection);
}
})
.onWillScroll((offset: number) => {
this.scrollOffset = this.scroller.currentOffset().yOffset + offset;
})
.children(LazyForEach(this.dataSource, (item: number) => {
if (item != 1) {
FlowItem() {
Column() {
Image(`./Image/${item % 10}.png`)
.objectFit(ImageFit.Cover)
.width('90%')
.height(100)
.layoutWeight(1)
.margin(5);
Text("必吃榜").fontSize(12).height('16');
}
}
.width('100%')
.height(this.itemHeightArray[item % 100])
.backgroundColor(Color.White);
}
}, (item: string) => item));
Stack() {
Column() {
// 吸顶部分
Text("吸顶内容")
.fontSize(20)
.fontColor(Color.Black)
.width('100%')
.height(100)
.hitTestBehavior(HitTestMode.Transparent)
.position({ x: 0, y: this.scrollOffset >= 220 ? 0 : 220 - this.scrollOffset });
}
}
.alignItems(HorizontalAlign.Start)
.backgroundColor(Color.White);
}
}
}
标签:item,number,WaterFlow,HarmonyOS,State,let,组件,100,sections
From: https://blog.51cto.com/u_15171169/12128570