基于WaterFlow的页面滑动加载

HarmonyOS官方账号
发布于 2024-9-27 09:31
浏览
0收藏

​场景描述

场景一:瀑布流页面多列混排的布局场景,例如:10个item在2列内布局,中间5个item在1列内撑满宽度布局,后10个item在3列内布局。

场景二:瀑布流页面中某一个Item可以固定展示在某一个位置,从而实现吸顶效果。

方案描述

场景一:

waterFlow支持自定义Item,从而在WaterFlow内完成多列混排的自定义布局,同时懒加载保证性能。

方案

通过sections配置分组信息,并初始化瀑布流分组,通过splice替换/添加新分组。

核心代码

1、计算FlowItem宽/高,设置FlowItem的宽/高数组,通过对应Item,设置FlowItem的宽/高。

2、通过sections配置分组信息,并初始化瀑布流分组,具体根据 SectionOptions需要自定义。可通过Item和分组分组信息中itemsCount设置指定Item的布局,例如Item=5,第一个分组中itemsCount: 4,Item=5为第二个分组的第二个Item(此处Item从0开始)。

3、即将触底时提前增加数据,添加加新分组到瀑布流末尾。​

//计算FlowItem宽/高 
getSize() { 
  let ret = Math.floor(Math.random() * this.maxSize) 
  return (ret > this.minSize ? ret : this.minSize) 
} 
// 设置FlowItem的宽/高数组 
setItemSizeArray() { 
  for (let i = 0; i < 100; i++) { 
    this.itemWidthArray.push(this.getSize()) 
    this.itemHeightArray.push(this.getSize()) 
  } 
} 
//配置分组信息,可通过Item和分组信息中itemsCount设置指定Item的布局,例如Item=5,第一个分组中为itemsCount: 4,Item=5为第二个分组的第二个Item(此处Item从0开始) 
@State sections: WaterFlowSections = new WaterFlowSections() 
oneColumnSection: SectionOptions = { 
  itemsCount: 4,//分组中FlowItem数量,必须是正整数。 
  crossCount: 1,//纵向布局时为列数,横向布局时为行数 
  columnsGap: 5,//该分组的列间距,不设置时使用瀑布流的columnsGap 
  rowsGap: 10,//该分组的行间距,不设置时使用瀑布流的rowsGap 
  margin: { top: 10, left: 5, bottom: 10, right: 5 }, 
  onGetItemMainSizeByIndex: (index: number) => {//瀑布流组件布局过程中获取指定index的FlowItem的主轴大小 
    return 150 
  } 
} 
twoColumnSection: SectionOptions = { 
  itemsCount: 20, 
  crossCount: 2, 
  onGetItemMainSizeByIndex: (index: number) => { 
    return this.itemHeightArray[index  100] 
  } 
} 
//初始化分组信息 
aboutToAppear() { 
  this.setItemSizeArray() 
  let sectionOptions: SectionOptions[] = [] 
  let count = 0 
  let oneOrTwo = 0 
  while (count < this.dataCount) { 
    //当oneOrTwo为偶数时加载第一个分组,为奇数时加载第二个分组 
    if (oneOrTwo++ % 2 == 0) { 
      sectionOptions.push(this.oneColumnSection) 
      count += this.oneColumnSection.itemsCount 
    } else { 
      sectionOptions.push(this.twoColumnSection) 
      count += this.twoColumnSection.itemsCount 
    } 
  } 
  //splice(start: number, deleteCount?: number, sections?: Array<SectionOptions>) 
  //start为正数时,表示从瀑布流首位开始计算,到start位置时,splicet替换现有分组 
  //start为负数时,表示从瀑布流末尾开始,splicet添加新分组 
  //deleteCount 表示要从start开始删除的分组数量 
  this.sections.splice(-1, 0, sectionOptions) 
} 
 
//即将触底时提前增加数据 
waterFlow({ scroller: this.scroller, sections: this.sections }) { 
  LazyForEach(this.dataSource, (item: number) => { 
    ... 
  } 
    .onScrollIndex((first: number, last: number) => { 
      // 即将触底时提前增加数据 
      //剩最后二十条数据时,提前增加数据,设置新的分组信息,将新分组添加到瀑布流末尾 
      if (last + 20 >= this.dataSource.totalCount()) { 
        for (let i = 0; i < 100; i++) { 
          this.dataSource.addLastItem() 
        } 
        let newSection: SectionOptions = { 
          itemsCount: 100, 
          crossCount: 2, 
          onGetItemMainSizeByIndex: (index: number) => { 
            return this.itemHeightArray[index % 100] 
          } 
        } 
        this.sections.push(newSection) 
      } 
    })

场景二:

页面中某个Item跟随页面滑动,到达吸顶位置时,继续滑动,吸顶元素保持不动,其他元素继续滑动。

方案

在瀑布流分组中为要吸顶的部分预留出位置,监听瀑布流滚动事件,吸顶部分基于瀑布流滚动后的偏移量设置位置,让吸顶部分跟随瀑布流一起滚动,吸顶部分到顶后固定不动。

核心代码

​1、在第一个分组中剔除Item=1,为吸顶部分留出位置,避免吸顶部分遮挡其余Item。

2、在数据渲染时也要剔除Item=1,其余Item正常渲染。

3、设置瀑布流的onWillScroll事件回调,监听瀑布流的滚动,吸顶部分基于瀑布流滚动后的偏移量设置位置,让吸顶部分跟随瀑布流一起滚动。吸顶部分初始位置为预留的Item=1的位置,吸顶部分到达顶部以后固定在顶部位置。​

//预留吸顶部分位置 
oneColumnSection: SectionOptions = { 
  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 
    } 
  } 
} 
..... 
WaterFlow({ scroller: this.scroller, sections: this.sections }) { 
  LazyForEach(this.dataSource, (item: number) => { 
    FlowItem() { 
      Column(){ 
        //剔除Item=1,为吸顶部分留出位置 
        //可基于Item和分组数量设置指定Item的布局,例如Item=3为第二个分组的第一个Item(此处Item从0开始) 
        if (item!=1) { 
          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) 
} 
..... 
//onWillScroll瀑布流滑动前触发,返回当前帧将要滑动的偏移量和当前滑动状态。返回的偏移量为计算得到的将要滑动的偏移量值,并非最终实际滑动偏移。 
//监听滚动事件,获取当前滚动偏移量和将要滚动的偏移量,scrollOffset保存滚动后的偏移量。 
.onWillScroll((offset:number) => { 
  this.scrollOffset = this.scroller.currentOffset().yOffset + offset //currentOffset 当前滚动的偏移量 
  //未滚动时this.scrollOffset初始值为0  
}) 
Stack() { 
  ........ 
}.backgroundColor(Color.White) 
}.alignItems(HorizontalAlign.Start) 
} 
.height(100) 
  .hitTestBehavior(HitTestMode.Transparent) 
    //this.scrollOffset滚动后的偏移量   滚动后的偏移量大于吸顶部分到顶部距离时,固定在顶部不动 
    //这里220为第一个Item高度200和第一个Item顶部和底部10的间隔 
  .position( {x: 0, y: this.scrollOffset >= 220 ? 0 : 220- this.scrollOffset })


分类
收藏
回复
举报
回复
    相关推荐