news 2026/7/27 19:46:59

HarmonyOS 6.0 自定义下拉刷新与上拉加载

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
HarmonyOS 6.0 自定义下拉刷新与上拉加载

列表没有下拉刷新就像网页没有F5——用户刷新不了数据,总觉得看到的是旧内容。HarmonyOS的Refresh组件能做基础刷新,但真要做出"下拉→弹性动画→加载中→收起"的完整体验,默认配置远远不够。上拉加载更是没有内置支持,得自己从头撸。

Refresh组件基础

最简单的下拉刷新:

@StateisRefreshing:boolean=falseRefresh({refreshing:$$this.isRefreshing}){List(){ForEach(this.items,(item:DataItem)=>{ListItem(){Text(item.title)}},(item:DataItem)=>item.id)}}.onRefreshing(()=>{this.loadData();})

$$this.isRefreshing是双向绑定——Refresh组件会自动设为true触发刷新,数据加载完后设为false收起。onRefreshing是刷新触发回调。

问题:Refresh的默认动画太简陋——就一个圈在转,没有弹性下拉效果,没有文字提示,没有成功/失败反馈。

自定义刷新布局

用Refresh的builder参数自定义刷新区域:

@StateisRefreshing:boolean=false@StaterefreshState:string='idle'@BuilderrefreshBuilder(){Row(){if(this.refreshState==='idle'){Text('下拉刷新').fontSize(14).fontColor('#999999')}elseif(this.refreshState==='pulling'){Text('释放立即刷新').fontSize(14).fontColor('#666666')}elseif(this.refreshState==='refreshing'){LoadingProgress().width(20).height(20).margin({right:8})Text('正在刷新...').fontSize(14).fontColor('#1a73e8')}elseif(this.refreshState==='success'){Text('刷新成功').fontSize(14).fontColor('#43a047')}elseif(this.refreshState==='failed'){Text('刷新失败,请重试').fontSize(14).fontColor('#e53935')}}.width('100%').height(60).justifyContent(FlexAlign.Center).alignItems(VerticalAlign.Center)}Refresh({refreshing:$$this.isRefreshing,builder:this.refreshBuilder()}){List(){// 列表内容}}.onRefreshing(async()=>{this.refreshState='refreshing';try{awaitthis.loadData();this.refreshState='success';}catch(e){this.refreshState='failed';}setTimeout(()=>{this.isRefreshing=false;this.refreshState='idle';},500);})

五个状态:idle→pulling→refreshing→success/failed→idle。成功和失败显示500ms后收起。

下拉距离监听

Refresh组件的onPullingDown回调提供下拉距离:

Refresh({refreshing:$$this.isRefreshing}){List(){// 内容}}.onPullingDown((progress:number)=>{if(progress<0.6&&this.refreshState!=='refreshing'){this.refreshState='idle';}elseif(progress>=0.6&&this.refreshState!=='refreshing'){this.refreshState='pulling';}})

progress是0~1的进度值,0表示未下拉,1表示到达触发阈值。0.6是个经验值——下拉超过60%时提示"释放刷新"。

自定义旋转箭头

下拉时箭头随进度旋转:

@StatepullProgress:number=0@BuilderrefreshBuilder(){Row(){Image($r('sys.media.ohos_ic_public_arrow_down')).width(20).height(20).rotate({angle:this.pullProgress*180}).animation({duration:100}).margin({right:8})if(this.refreshState==='refreshing'){LoadingProgress().width(20).height(20).margin({right:8})Text('加载中...')}elseif(this.pullProgress>=0.6){Text('释放刷新')}else{Text('下拉刷新')}}.fontSize(14).fontColor('#666666').width('100%').height(60).justifyContent(FlexAlign.Center)}.onPullingDown((progress:number)=>{this.pullProgress=progress;// 状态判断})

pullProgress从0到1,箭头旋转0°到180°。超过阈值时箭头朝上,提示"释放刷新"。

上拉加载更多

HarmonyOS没有内置上拉加载,需要自己实现。最简单的方式是在List底部加一个加载触发器:

@StateisLoadingMore:boolean=false@StatehasMore:boolean=trueprivatecurrentPage:number=1List(){ForEach(this.items,(item:DataItem)=>{ListItem(){// 列表项内容}},(item:DataItem)=>item.id)// 底部加载指示器if(this.hasMore){ListItem(){Row(){LoadingProgress().width(20).height(20).margin({right:8})Text('加载更多...').fontSize(14).fontColor('#999999')}.width('100%').height(50).justifyContent(FlexAlign.Center)}}else{ListItem(){Text('没有更多了').fontSize(14).fontColor('#cccccc').width('100%').textAlign(TextAlign.Center).padding(16)}}}.onReachEnd(()=>{if(!this.isLoadingMore&&this.hasMore){this.loadMore();}})

onReachEnd在列表滚动到底部时触发。isLoadingMore防止重复触发,hasMore判断是否还有更多数据。

loadMore的实现:

privateasyncloadMore():Promise<void>{this.isLoadingMore=true;this.currentPage++;try{letnewItems=awaitthis.fetchData(this.currentPage);if(newItems.length===0){this.hasMore=false;}else{this.items=this.items.concat(newItems);}}catch(e){this.currentPage--;}this.isLoadingMore=false;}

concat合并新旧数据。新数据为空说明到底了,设hasMore=false。

手动触发加载更多

onReachEnd在快速滚动时可能不触发。可以用onScrollIndex作为补充:

.onScrollIndex((start:number,end:number)=>{if(end>=this.items.length-3&&!this.isLoadingMore&&this.hasMore){this.loadMore();}})

距离底部3条数据时提前加载,实现"预加载"效果。用户看到最后几条时新数据已经准备好了。

空状态处理

首次加载和刷新后都可能有空结果:

if(this.items.length===0){Column(){if(this.isFirstLoad){LoadingProgress().width(40).height(40).margin({bottom:16})Text('加载中...').fontSize(14).fontColor('#999999')}else{Image($r('app.media.empty_state')).width(120).height(120).margin({bottom:16})Text('暂无数据').fontSize(16).fontColor('#999999').margin({bottom:8})Text('点击重试').fontSize(14).fontColor('#1a73e8').onClick(()=>{this.isRefreshing=true;this.loadData();})}}.width('100%').height('100%').justifyContent(FlexAlign.Center)}else{Refresh({refreshing:$$this.isRefreshing}){List(){// 列表内容}}.onRefreshing(()=>{this.loadData();})}

首次加载显示loading,加载完成无数据显示空状态+重试按钮。有数据时才显示列表。

刷新与加载的协调

下拉刷新应该重置分页状态:

privateasyncloadData():Promise<void>{this.currentPage=1;this.hasMore=true;try{this.items=awaitthis.fetchData(1);this.refreshState='success';}catch(e){this.refreshState='failed';}setTimeout(()=>{this.isRefreshing=false;this.refreshState='idle';},500);}

刷新时currentPage重置为1,hasMore恢复true,items用新数据替换(不是concat)。加载更多时才concat追加。

关键:刷新和加载更多不能同时进行。在isRefreshing为true时禁止触发loadMore,在isLoadingMore为true时不响应onRefreshing。

列表滚动位置记忆

刷新后列表会跳回顶部。要保持位置:

privatescroller:Scroller=newScroller();privatescrollOffset:number=0;List(){// 内容}.scroller(this.scroller).onScroll(()=>{this.scrollOffset=this.scroller.currentOffset().yOffset;})// 刷新完成后恢复位置asyncloadData():Promise<void>{letsavedOffset=this.scrollOffset;this.currentPage=1;this.items=awaitthis.fetchData(1);this.isRefreshing=false;// 恢复滚动位置setTimeout(()=>{this.scroller.scrollTo({xOffset:0,yOffset:savedOffset});},100);}

刷新前记录currentOffset,刷新后scrollTo恢复。setTimeout 100ms等UI更新完成再滚动。

但大多数应用刷新后跳回顶部是预期行为——用户下拉刷新就是想看最新内容。只有"静默刷新"(后台刷新不跳位置)才需要记忆位置。

踩坑清单

问题原因解决
刷新动画收不起来isRefreshing没设回falseloadData完成后设false
下拉没有弹性没用Refresh组件用Refresh包裹List
加载更多重复触发没用isLoadingMore防重加布尔锁
没有更多数据还触发hasMore没判断加hasMore标记
刷新时触发加载更多两个状态没互斥isRefreshing时禁止loadMore
onReachEnd不触发LazyForEach缓存不够用onScrollIndex提前触发
空状态没显示只判断items空没判断加载状态区分首次加载和加载完成
刷新后列表跳顶这是默认行为如需保持用scroller记忆位置
自定义builder不显示builder参数写法错builder: this.xxxBuilder()
下拉进度不准确onPullingDown的progress是0~1progress>=0.6为可触发

下拉刷新用Refresh+自定义builder,上拉加载用onReachEnd+分页控制。两者共享数据源但状态独立——刷新重置分页,加载追加数据。记住这个原则,任何列表的刷新加载都能搞定。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/27 19:43:01

Potree点云可视化终极指南:如何用WebGL处理大规模三维数据

Potree点云可视化终极指南&#xff1a;如何用WebGL处理大规模三维数据 【免费下载链接】potree WebGL point cloud viewer for large datasets 项目地址: https://gitcode.com/gh_mirrors/po/potree Potree是一个基于WebGL的开源点云渲染器&#xff0c;专门用于处理海量…

作者头像 李华
网站建设 2026/7/27 19:42:56

智能废品回收系统:AI与算法驱动的环保解决方案

1. 项目背景与核心价值最近在帮朋友改造他们小区的废品回收流程&#xff0c;发现传统方式存在不少痛点。居民要么把可回收物直接扔进垃圾桶&#xff0c;要么得等流动回收车不定时来收&#xff0c;既浪费资源又不环保。这个小程序就是为了解决这些问题而设计的。这个二手废品回收…

作者头像 李华
网站建设 2026/7/27 19:42:39

【面向对象】面向对象基本概念:对象、类、消息

考点频率&#xff1a;★★★★★&#xff08;年年必考&#xff0c;选择题的“送分题”也是“送命题”&#xff09; 难度&#xff1a;⭐ 建议&#xff1a;死磕“对象三要素”和“类与对象的区别”&#xff0c;这是你面向对象大厦的“地基钢筋” 1️⃣ 咱们先别背定义&#xff0c…

作者头像 李华