HarmonyOS 实战开发 —— 基于WaterFlow的页面滑动加载


📚往期笔录记录🔖:

🔖鸿蒙(HarmonyOS)北向开发知识点记录~
🔖鸿蒙(OpenHarmony)南向开发保姆级知识点汇总~
🔖鸿蒙应用开发与鸿蒙系统开发哪个更有前景?
🔖嵌入式开发适不适合做鸿蒙南向开发?看完这篇你就了解了~
🔖对于大前端开发来说,转鸿蒙开发究竟是福还是祸?
🔖鸿蒙岗位需求突增!移动端、PC端、IoT到底该怎么选?
🔖记录一场鸿蒙开发岗位面试经历~
🔖持续更新中……


场景描述

  • 场景一:瀑布流页面多列混排的布局场景,例如: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 })
<think>好的,我需要帮助用户在HarmonyOS开发购物软件的用户界面。首先,我需要整理用户的问题,明确他们想知道的是UI设计和实现的具体方法。根据用户提供的引用资料,特别是引用[1]和引用[2],里面有提到ArkTS、ArkUI组件、多端部署以及高性能瀑布流页面的案例。这些都是关键点。 首先,我应该从HarmonyOS的UI开发基础开始。ArkUI是必须提到的,因为它是核心框架。需要解释声明式开发范式和组件化设计,可能还需要提到布局组件,比如Flex、Grid、List等,这些都是构建购物应用界面常用的。 接下来是购物应用的核心功能模块设计。比如首页的商品展示,这里可以结合引用[2]中的瀑布流案例,使用FlashList或者类似组件来实现高性能的列表。商品详情页需要图片轮播、商品信息展示和购买按钮,这里需要用到Swiper组件和自定义弹窗。购物车页面需要列表展示和底部结算栏,可能涉及数据绑定和状态管理。 然后是UI实现的关键技术点。高性能列表部分,引用[2]提到了基于RN框架的FlashList和FastImage,需要说明如何应用这些组件优化性能。跨端适配也是重点,HarmonyOS支持多端部署,应该提到响应式布局和媒体查询的使用方法。动效部分,可以引用ArkUI的内置动画组件,比如属性动画或转场动画,提升用户体验。 还需要考虑数据层和UI的交互,比如商品数据的加载和展示,可能需要结合axios进行网络请求,确保数据实时更新。状态管理方面,使用AppStorage或LocalStorage来管理购物车数据,保持状态同步。 最后,相关问题的生成需要围绕用户可能进一步询问的方向,比如性能优化、多端适配、动效实现和组件封装,这些都是开发中常见的问题点。确保每个问题都能引导用户深入理解相关技术点。 整个过程中,要确保遵循系统级指令,正确使用LaTeX格式,所有行内公式用$...$,独立公式用$$...$$。同时,引用资料中的内容要在适当的位置添加引用标识,比如在提到瀑布流案例时引用[^2],在提到鸿蒙核心知识时引用[^1]。 可能需要注意的细节:检查代码示例是否正确,是否使用ArkTS语法,比如@Component装饰器和struct结构。确保提到的组件和API与HarmonyOS NEXT的文档一致,避免过时信息。此外,性能优化部分需要具体,比如列表的懒加载、图片缓存策略,这些都能提升用户体验。 总结下来,回答的结构应该是先介绍UI基础,再分模块讲解设计,接着关键技术实现,最后代码示例和相关问题。确保每一步都清晰,并且结合引用资料中的案例和技术点,让用户能够根据指引进行实际开发。</think>在HarmonyOS开发购物软件的用户界面,需结合ArkUI框架和声明式开发范式,重点围绕商品展示、交互设计和性能优化展开。以下是具体实现步骤: ### 一、HarmonyOS UI开发基础 1. **ArkUI框架核心** - 使用声明式开发范式,通过`@Component`装饰器定义可复用组件 - 采用Flex/Grid布局实现响应式设计: ```typescript Flex({ direction: FlexDirection.Row, wrap: FlexWrap.Wrap }) { ForEach(this.productList, (item: Product) => { ProductItem({ data: item }) }) } .width('100%') .padding(10) ``` - 通过`@State`、`@Prop`等装饰器实现数据驱动视图更新[^1] 2. **界面架构设计** ```typescript @Entry @Component struct ShoppingApp { @State currentTab: number = 0 build() { Tabs({ barPosition: BarPosition.Bottom }) { TabContent({ tabBar: '首页' }) { HomePage() } TabContent({ tabBar: '分类' }) { CategoryPage() } TabContent({ tabBar: '购物车' }) { CartPage() } TabContent({ tabBar: '我的' }) { ProfilePage() } } } } ``` ### 二、购物应用核心功能实现 1. **首页商品瀑布流** - 使用`<List>`或第三方`FlashList`组件实现高性能滚动 - 结合`<WaterFlow>`布局组件: ```typescript @Component struct ProductGrid { @Prop products: Array<Product> build() { GridRow({ columns: { sm: 2, md: 3, lg: 4 }, gutter: 8 }) { ForEach(this.products, (item) => { GridCol({ span: { sm: 1 } }) { ProductCard(item) } }) } } } ``` - 通过`<Refresh>`组件实现下拉刷新[^2] 2. **商品详情页实现** ```typescript @Component struct ProductDetail { @State currentImageIndex: number = 0 build() { Scroll() { Swiper() { ForEach(this.product.images, (img) => { Image(img) .objectFit(ImageFit.Contain) }) } .autoPlay(false) .indicator(true) Text(this.product.title) .fontSize(24) // 其他商品信息组件 } } } ``` ### 三、关键技术实现要点 1. **性能优化方案** - 使用`<LazyForEach>`实现列表项懒加载 - 通过`FastImage`组件实现图片缓存: ```typescript import { FastImage } from '@react-native-oh-tpl/react-native-fast-image' FastImage({ source: { uri: item.imageUrl }, fallback: true }) .size({ width: '100%', height: 200 }) ``` - 采用`willReachBottom`事件监听实现分页加载 2. **交互动效实现** - 购物车添加动画: ```typescript @Component struct AddToCartButton { @State scaleVal: number = 1 build() { Button('加入购物车') .scale({ x: this.scaleVal, y: this.scaleVal }) .onClick(() => { animateTo({ duration: 200, curve: Curve.EaseOut }, () => { this.scaleVal = 0.8 this.scaleVal = 1 }) }) } } ``` ### 四、开发资源推荐 1. 使用DevEco Studio的实时预览功能调试UI布局 2. 参考官方`Sample`中的[购物应用示例](https://gitee.com/harmonyos/samples) 3. 使用`@ohos/axios`处理网络请求,确保数据层与UI解耦 ```typescript // 商品数据请求示例 import axios from '@ohos/axios' axios.get('/api/products') .then((response) => { this.productList = response.data }) .catch((error) => { console.error('请求失败:', error) }) ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值