尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

鸿蒙新特性——Refresh 下拉刷新组件详解

鸿蒙新特性——Refresh 下拉刷新组件详解
📅 发布时间:2026/7/6 5:33:47

一、引言

在移动应用中,"下拉刷新"是最核心的交互模式之一。无论是微博首页的最新动态、微信朋友圈的新鲜内容、还是资讯 App 的实时新闻,用户都习惯性地下滑列表顶部,期待看到最新的数据更新。"下拉→加载→新内容出现"这个流畅的动作序列,已经成为移动端内容消费的肌肉记忆。

然而,实现下拉刷新并非简单地在列表顶部挂一个加载动画。开发者需要精确处理:下拉距离与视觉反馈的对应关系(下拉多远才触发刷新?回弹动画如何衔接?)、刷新状态管理(空闲→拖动→刷新中→完成→重置)、手势冲突(列表自身的滚动与下拉手势如何区分?)、以及多场景适配(列表为空时要不要显示刷新?网络失败时如何提示?)。

而 HarmonyOS 提供了Refresh组件——一个专用于下拉刷新的容器组件。它包裹在列表内容外层,自动处理手势识别、回弹动画和加载状态切换。开发者只需声明refreshing状态(通过$$双向绑定)并在onRefreshing回调中执行数据加载逻辑,即可获得完整的下拉刷新体验。

本文通过一个"动态资讯"Demo 深入讲解 Refresh 组件的核心用法:如何创建下拉刷新列表?refreshing状态如何通过$$双向绑定?onRefreshing回调如何配合数据加载?以及如何实现手动刷新、展开详情等完整交互。

阅读完本文,你将能够:

  • 使用 Refresh 组件实现下拉刷新功能
  • 使用$$双向绑定管理 refreshing 状态
  • 在onRefreshing回调中执行异步数据加载
  • 将 Refresh 与 List 组件结合构建资讯流
  • 实现手动刷新按钮作为下拉刷新的补充交互

二、Refresh 组件 API 总览

2.1 构造函数

Refresh(options:RefreshOptions){// 子内容:通常是一个 List、Grid 或 Scroll}
interfaceRefreshOptions{refreshing:boolean;// 当前的刷新状态}
参数类型说明
refreshingbooleantrue 时显示刷新动画(loading 旋转图标),false 时隐藏。通常使用$$前缀实现双向绑定

$$是 ArkUI 提供的双向绑定语法糖,格式为$$this.stateVar。它同时完成了两件事:将stateVar的值传递给组件,以及当组件内部改变该值时自动同步回stateVar。在 Refresh 中,用户下拉超过阈值时组件会自动将refreshing设为 true,数据加载完成后开发者手动将其设回 false。

2.2 链式方法

// 刷新状态变化回调(用户下拉触发时调用).onRefreshing(callback:()=>void):RefreshAttribute// 刷新状态变化(包含完整的生命周期状态).onStateChange(callback:(state:RefreshStatus)=>void):RefreshAttribute// 下拉触发阈值距离(vp).offset(value:number|string|Resource):RefreshAttribute// 自定义下拉时的显示内容.refreshContent(content:()=>void):RefreshAttribute
方法说明
.onRefreshing(Callback)下拉触发的回调。在此回调中执行数据加载(网络请求、数据库查询等),完成后将 refreshing 设回 false
.onStateChange(Callback<RefreshStatus>)刷新状态的完整生命周期回调,包含 Drag(拖动)、Refreshing(刷新中)、Done(完成)三种状态。用于更细粒度的 UI 反馈(如"释放立即刷新"文案切换)
.offset(number)下拉触发阈值,单位 vp。默认值约为 64vp。设置越大需要下拉越深才触发刷新
.refreshContent(CustomBuilder)完全自定义下拉区域的显示内容,替代默认的 loading 旋转图标。可嵌入自定义图标、文字提示等

2.3 RefreshStatus 枚举

enumRefreshStatus{Inactive,// 未激活(空闲状态)Drag,// 正在拖动(下拉中,未达阈值)OverDrag,// 超过阈值(可释放触发刷新)Refresh,// 正在刷新(加载动画显示中)Done// 刷新完成}
状态说明UI 反馈示例
Inactive空闲,列表未下拉无特殊指示
Drag正在下拉,未超过阈值“下拉刷新” 文字
OverDrag超过阈值,松手即触发“释放立即刷新” 文字 + 箭头翻转动画
Refresh正在加载数据loading 旋转图标 + “加载中…”
Done加载完成,即将收起loading 消失,内容区域回弹

Demo 中使用了onRefreshing(核心回调)管理加载逻辑,考虑到 Demo 的简洁性未使用onStateChange。对于需要"松手刷新"提示文案的产品级应用,建议使用onStateChange监听 Drag 和 OverDrag 状态。

2.4 Refresh 与手动刷新按钮的配合

Refresh 组件的refreshing状态默认为 false。当开发者通过代码设置this.isRefreshing = true(例如点击"手动刷新"按钮),即使没有下拉手势,Refresh 组件也会进入刷新状态(显示 loading 动画),等待加载完成后设回 false。

这意味着一套刷新逻辑(doRefresh()方法)可以同时服务于两个触发源:下拉手势(通过onRefreshing回调调用)和手动按钮(通过onClick直接调用)。这是 Demo 中的关键设计——用户既可以通过下拉刷新获取最新资讯,也可以点击顶部"手动刷新"按钮触发加载。

三、Demo 设计:动态资讯

3.1 功能概述

Demo 是一个"动态资讯"应用,模拟资讯类 App 的新闻信息流:

  1. 资讯列表:15 条预置新闻内容池,每次加载随机抽取。每条新闻包含分类标签、来源、发布时间、标题和摘要
  2. 下拉刷新:下拉列表顶部触发刷新,loading 1.5 秒后新增 3 条随机新闻插入列表头部
  3. 手动刷新:顶部状态栏"手动刷新"按钮触发相同加载逻辑
  4. 上次刷新时间:每次刷新后更新时间,格式为"HH:MM"
  5. 展开/收起详情:点击"展开全文"查看新闻摘要,点击"收起"折叠
  6. 列表滚动:Refresh 包裹的 List 支持完整滚动交互

3.2 交互点

#交互说明
1下拉刷新下滑列表顶部触发 onRefreshing → 加载 3 条新资讯 → 插入列表头部
2手动刷新点击"手动刷新"按钮,同样触发加载逻辑,更新刷新时间
3展开详情点击新闻卡片"展开全文"→ 显示完整摘要 → "收起"折叠
4滚动浏览List 中滚动浏览全部资讯,分类标签和文章来源辅助筛选

四、完整代码实现

4.1 数据模型与状态

interfaceNewsItem{id:number;title:string;summary:string;source:string;time:string;category:string;catColor:string;}@StateisRefreshing:boolean=false;@StatelastRefreshTime:string='暂无';@StatenewsList:NewsItem[]=[];@StateexpandedId:number=-1;privatenewsPool:NewsItem[]=[];// 15 条预置新闻

NewsItem包含新闻的完整信息:标题、摘要(展开后显示)、来源、发布时间、分类(带颜色标签)。newsPool存储全部 15 条预置新闻作为数据池,newsList是当前显示的列表(初始 5 条随机新闻)。expandedId记录当前展开的新闻 ID(-1 表示无展开)。

4.2 Refresh 包裹 List

Refresh({refreshing:$$this.isRefreshing}){List(){ForEach(this.newsList,(news:NewsItem,idx:number)=>{ListItem(){Column(){Row(){Text(news.category).fontSize(10).fontColor('#FFFFFF').fontWeight(FontWeight.Bold).padding({top:2,bottom:2,left:6,right:6}).borderRadius(4).backgroundColor(news.catColor)Text(news.source).fontSize(11).fontColor('#BBBBCC').margin({left:8})Blank()Text(news.time).fontSize(11).fontColor('#BBBBCC')}.width('100%')Text(news.title).fontSize(16).fontColor('#1a1a2e').fontWeight(FontWeight.Medium).maxLines(2).textOverflow({overflow:TextOverflow.Ellipsis}).width('100%').margin({top:10,bottom:this.expandedId===news.id?10:6})if(this.expandedId===news.id){Text(news.summary).fontSize(13).fontColor('#666677').lineHeight(20).width('100%').margin({bottom:10})}Row(){Text(this.expandedId===news.id?'收起':'展开全文').fontSize(11).fontColor('#1677FF').fontWeight(FontWeight.Medium).onClick(()=>{this.toggleExpand(news.id);})}}.width('100%').padding({left:Spacing.LG,right:Spacing.LG,top:14,bottom:14})}.border({width:{bottom:0.5},color:'#F2F3F5'})},(news:NewsItem,idx:number)=>news.id.toString())}.width('100%').height('100%').scrollBar(BarState.Off)}.onRefreshing(()=>{this.doRefresh();})

逐行解析:

  • Refresh({ refreshing: $$this.isRefreshing }):$$双向绑定isRefreshing状态。用户下拉超过阈值时,组件自动将isRefreshing设为 true,触发 loading 动画;加载完成后开发者将isRefreshing设回 false
  • List():Refresh 内部包裹一个 List 组件作为可滚动内容。List 每项是ListItem(),通过ForEach从newsList数据数组生成
  • .onRefreshing(() => { this.doRefresh(); }):下拉触发回调,调用doRefresh()执行数据加载
  • 展开/收起逻辑:expandedId记录当前展开项,toggleExpand()切换expandedId。展开时显示news.summary(约 80~100 字摘要),折叠时仅显示标题

4.3 数据加载逻辑

doRefresh():void{this.isRefreshing=true;setTimeout(()=>{constfreshNews=this.randomNews(3);constnewList:NewsItem[]=[];for(leti=0;i<freshNews.length;i++){newList.push(freshNews[i]);}for(leti=0;i<this.newsList.length;i++){newList.push(this.newsList[i]);}this.newsList=newList;constnow=newDate();this.lastRefreshTime=now.getHours().toString().concat(':',now.getMinutes().toString().length===1?'0'.concat(now.getMinutes().toString()):now.getMinutes().toString());this.isRefreshing=false;},1500);}

doRefresh()是核心加载方法,既由onRefreshing回调触发(下拉手势),也由"手动刷新"按钮的onClick触发:

  1. 设置刷新状态:this.isRefreshing = true激活 loading 动画
  2. 模拟网络请求:setTimeout延迟 1.5 秒模拟网络加载延迟
  3. 随机抽取新内容:randomNews(3)从 15 条预置新闻中随机抽取 3 条不重复的新闻
  4. 插入列表头部:将新 3 条新闻放在newList头部,原有内容拼接在后(newList.push(this.newsList[i]))
  5. 更新刷新时间:获取当前时间的"HH:MM"格式,处理分钟补零(如 9:05)
  6. 结束刷新:this.isRefreshing = false关闭 loading 动画,Refresh 组件自动播放回弹动画

需要注意:数组更新遵循 ArkUI 的不可变数据模式——创建新的数组newList而非修改原数组,确保@State能检测到变化并触发 UI 更新。

4.4 手动刷新按钮

Row(){Text('上次刷新:'.concat(this.lastRefreshTime)).fontSize(11).fontColor('#9999AA')Blank()Text('手动刷新').fontSize(11).fontColor('#1677FF').fontWeight(FontWeight.Medium).padding({top:3,bottom:3,left:10,right:10}).borderRadius(10).backgroundColor('#EEF3FF').onClick(()=>{this.doRefresh();})}.width('100%').height(36).padding({left:Spacing.LG,right:Spacing.LG}).backgroundColor('#F2F3F5')

状态栏左侧显示"上次刷新:HH:MM"提供时间参考,右侧"手动刷新"按钮直接调用doRefresh()。这展示了 Refresh 组件的一个重要特性:刷新逻辑与触发方式解耦。同一个doRefresh()方法,通过下拉手势触发和按钮点击触发都能正确工作——因为$$双向绑定确保了两种触发路径下isRefreshing状态的一致性。

4.5 新闻展开/折叠

toggleExpand(id:number):void{if(this.expandedId===id){this.expandedId=-1;// 收起:清除展开状态}else{this.expandedId=id;// 展开:记录当前展开项}}

toggleExpand()实现"手风琴"式展开行为——同一时间只有一个新闻项处于展开状态。点击新的"展开全文"会自动收起之前展开的项。展开时标题下方的 margin 从 6vp 扩大到 10vp,为摘要文本留出视觉间距。

五、关键技术点详解

5.1 $$ 双向绑定的底层机制

$$this.isRefreshing是 ArkUI 声明式框架的核心特性之一。它等价于同时做两件事:

  1. 属性传递:refreshing: this.isRefreshing—— 将当前isRefreshing的值传给 Refresh 组件
  2. 事件监听:当 Refresh 组件内部改变refreshing时(如下拉触发),自动调用this.isRefreshing = newValue同步回状态变量

需要注意的是,$$只能用于@State、@Prop、@Link等可观察状态变量,不能用于普通private变量。此外,在$$模式下,开发者不应在onRefreshing回调中再次设置isRefreshing = true(因为组件已经自动设置了),而应该在数据加载完成后设置isRefreshing = false。

不过 Demo 中doRefresh()同时服务于下拉和手动按钮,所以包含了isRefreshing = true。对于下拉触发的路径,这个赋值是冗余的(一次无副作用的相同值写入),对于按钮触发的路径则是必要的。

5.2 onRefreshing 的触发时机

onRefreshing在下拉距离超过 Refresh 组件的触发阈值时自动调用。具体触发流程:

  1. 用户在列表顶部继续向下拖动
  2. 下拉距离逐渐增加,Refresh 组件内部计算偏移量
  3. 当偏移量超过阈值(默认约 64vp),组件判定用户意图为"刷新"
  4. 组件内部将refreshing设为 true(通过$$同步到状态变量)
  5. 调用onRefreshing回调
  6. 开发者执行数据加载,完成后将refreshing设回 false

如果在步骤 4 之前用户松手(下拉不够深),Refresh 组件会自动播放回弹动画回到顶部,不会触发刷新。这个"下拉距离阈值"可以通过.offset(value)自定义,单位为 vp。

5.3 列表为空时的刷新行为

当列表内容为空(newsList为空数组)时,Refresh 组件仍然可以触发刷新。此时的交互略有不同:

  • 有内容时:用户需要将列表滚动到顶部再继续下拉(两次手势——先上滑、再下拉),或者列表本身就在顶部时直接下拉
  • 空列表时:Refresh 占据整个容器空间,用户在任何位置下拉都能触发刷新

这意味着 Refresh 在空列表场景下更加灵敏——非常适合"首次加载"和"数据清空后重新加载"的场景。Demo 中初始状态有 5 条新闻(不为空),但删除所有新闻后列表为空,Refresh 依然能正确触发。

5.4 手动刷新与下拉刷新的 UX 互补

在产品设计中,不应仅依赖下拉刷新。以下是常见的补充触发方式:

触发方式适用场景优点缺点
下拉刷新列表在顶部时符合直觉,手势自然需要将列表滚到顶部才能触发
手动按钮(Demo 中使用)任意滚动位置无需滚回顶部,即时触发占用额外屏幕空间
自动轮询实时性要求高的场景(聊天、交易)零用户操作耗电、耗流量
长按菜单"刷新"工具栏/TabBar 中的刷新选项不需额外空间发现性差

Demo 中使用"下拉刷新 + 手动按钮"双触发模式:快速浏览时随手势下拉刷新(自然),精确操作时点击按钮(快捷)。

5.5 自定义 refreshContent

默认的刷新动画是一个简单的 loading 旋转图标。对于品牌化需求,可以使用.refreshContent()自定义下拉区域的显示内容:

Refresh({refreshing:$$this.isRefreshing}){List(){...}}.refreshContent(()=>{this.customRefreshBuilder()})

customRefreshBuilder可以是包含图标、文字、动画的任意组件组合。例如"下拉刷新" / "释放立即刷新"的状态切换 + 品牌 Logo 动画。Demo 中使用默认样式以保持简洁,但产品级应用建议自定义以增强品牌识别度。

5.6 性能考量:高频刷新与数据更新策略

刷新操作可能被高频触发(用户反复下拉),需要注意以下性能点:

  1. 防抖(Debounce):如果onRefreshing中触发的是真实网络请求,建议加入防抖逻辑——在刷新动画进行中时忽略新的刷新请求。Demo 中isRefreshing本身起到了一定防护作用(在true状态时重复触发doRefresh()不会产生新动画)

  2. 不可变数据更新:列表数据更新时必须使用新数组(newList = [...])而非this.newsList.push(item),否则@State无法检测到变化导致 UI 不刷新。Demo 中演示了正确的不可变更新模式

  3. 列表 key 函数:ForEach的第三个参数(key 函数)帮助框架识别哪些列表项发生了变化、哪些可以复用。Demo 中使用news.id.toString()作为唯一 key,确保新增项正确渲染、已有项节点复用

  4. 控制列表长度:实际产品中列表可能包含数百条数据。每次刷新插入新数据后,如果列表无限增长,应考虑分页加载 + 旧数据清理策略(如保留最近 50 条)

六、运行效果

6.1 初始状态

进入"动态资讯"页面,顶部状态栏显示"上次刷新:暂无"和蓝色"手动刷新"按钮。下方白色说明卡片介绍 Refresh 组件。资讯列表展示 5 条随机新闻,每条包含彩色分类标签(蓝/绿/橙/紫/红)、来源名称(如"华为官方"“开发者社区”)、发布时间和标题。底部有 0.5vp 分隔线。

6.2 下拉刷新

将列表滑到顶部,继续向下拖动 → Refresh 组件检测到超过阈值 → loading 旋转图标出现在列表上方 →onRefreshing触发doRefresh()→ 1.5 秒后 3 条新随机新闻插入列表头部 → loading 消失、列表回弹 → 上次刷新时间更新为当前 HH:MM。

6.3 手动刷新

滚动到列表中间位置 → 点击顶部"手动刷新"按钮 → loading 动画立即显示(无需下拉手势)→ 1.5 秒后 3 条新新闻插入 → 刷新时间更新 → loading 消失。手动刷新与下拉刷新使用完全相同的doRefresh()逻辑,效果一致。

6.4 展开详情

点击任意新闻的"展开全文"→ 该条新闻扩展显示完整摘要(约 80~100 字中文描述),“展开全文"变为"收起”。点击"收起"→ 摘要折叠,仅显示标题。点击另一条新闻的"展开全文"→ 之前展开的自动折叠,新点击的展开(同一时间仅一项展开)。

6.5 多次刷新效果

连续点击 3 次"手动刷新"→ 每次插入 3 条,列表顶部累积 9 条新新闻。滚动浏览所有内容 → 分类标签颜色帮助快速区分新闻类型。列表流畅滚动无卡顿。

七、总结

本文通过一个"动态资讯"实战 Demo,深入讲解了 HarmonyOS Refresh 下拉刷新组件的核心用法:

  1. Refresh 容器:包裹 List 内容,自动处理下拉手势识别和回弹动画
  2. $$双向绑定:$$this.isRefreshing实现刷新状态与 Refresh 组件的自动同步,组件感知状态变化,状态反映组件内部变化
  3. onRefreshing回调:下拉触发的数据加载入口,在此执行异步加载并在完成后将 refreshing 设回 false
  4. 手动刷新:按钮直接调用同一doRefresh()方法,展示"一套逻辑、多种触发"的设计模式
  5. 不可变数据更新:每次刷新创建新数组插入头部,确保@State检测到变化触发 UI 更新

Refresh 将"手势识别→阈值判断→加载动画→数据更新→回弹收起"这一整套下拉刷新的交互流程封装进一个容器组件,开发者只需声明状态绑定和加载回调即可获得完整的下拉刷新体验。希望本文能帮助你在实际项目中高效运用 Refresh 组件。


本文基于 HarmonyOS NEXT API 24 编写,代码经 DevEco Studio 6.1.1 编译验证通过。

相关新闻

  • 专业构建精简Windows 11镜像的5步完整指南:从臃肿系统到高效工作站的蜕变
  • 如何用一个API搞定六大音乐平台?Listen1 API跨平台音乐聚合终极指南
  • 实战指南:如何构建高性能Android电视媒体中心 - VLC电视版深度配置与优化

最新新闻

  • 工业4-20mA电流环与INA196电流检测设计实践
  • LTC6904与PIC18LF27K40实现高精度可编程方波发生器
  • 基于STM32与PWM控制器的数字降压电源设计
  • ComfyUI-VideoHelperSuite终极指南:5步轻松掌握AI视频处理
  • Grouped GEMM 为什么适合 MoE?从碎片小矩阵到通信友好的算子设计
  • 完全免费解锁Wand专业版:终极本地增强方案深度指南

日新闻

  • AI智能体安全防护框架AgentGuard:从原理到实战部署指南
  • KMX63与PIC18F26K40硬件组合及低功耗设计实践
  • 基于YOLO13改进的门体检测模型:C3k2模块与PoolingFormer技术解析

周新闻

  • 基于YOLOv12的番茄成熟度智能检测系统开发
  • 终极RimWorld模组管理指南:用RimSort告别模组冲突烦恼
  • AI Agent框架开发:从理论到实践的完整指南

月新闻

  • 2026年6月公司网站搭建最新热门渠道测评:四大低成本/零代码平台对比+避坑
  • 【Linux】Linux arm 编译QT程序,出现expected “}“报错
  • 【MATLAB例程】四基站二维AOA定位与距离辅助增强对比仿真。基于角度观测和测距修正的固定目标平面定位精度分析

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号