HarmonyOS 鸿蒙Next中如何实现下拉刷新功能
HarmonyOS 鸿蒙Next中如何实现下拉刷新功能
如何实现下拉刷新功能
【背景知识】
- PullToRefresh是一款OpenHarmony环境下可用的下拉刷新、上拉加载组件。支持设置内置动画的各种属性,支持设置自定义动画,支持lazyForEarch的数据作为数据源。
- PullToRefresh使用限制:
- 目前只支持List、Scroll、Tabs、Grid和WaterFlow系统容器组件;
- 暂不支持设置系统容器组件的弹簧效果和阴影效果,使用时需要将系统组件edgeEffect属性的值设置为(EdgeEffect.None);
- 暂不支持页面触底时自动触发上拉加载功能;
- 暂不支持在页面数据不满一屏时触发上拉加载功能;
- 暂不支持通过代码的方式去触发下拉刷新功能;
- 暂不支持在下拉刷新动画结束时提供手势结束的回调。
- Refresh:可以进行页面下拉操作并显示刷新动效的容器组件。
- List组件:列表包含一系列相同宽度的列表项。
- Button组件:按钮组件通常用于响应用户的点击操作,其类型包括胶囊按钮、圆形按钮、普通按钮、圆角矩形按钮。Button做为容器使用时可以通过添加子组件实现包含文字、图片等元素的按钮。
- $$运算符:$$运算符为系统组件提供TS变量的引用,使得TS变量和系统组件的内部状态保持同步。
【解决方案】
- 场景一解决方案: 在项目根目录下的oh-package.json5文件中配置PullToRefresh:
{
// ...
"devDependencies": {
"[@ohos](/user/ohos)/pulltorefresh": "2.1.2"
}
}
在模块目录下的oh-package.json5文件中配置PullToRefresh:
{
"name": "entry",
// ...
"dependencies": {
"[@ohos](/user/ohos)/pulltorefresh": "^2.1.2"
}
}
示例代码实现:
import { PullToRefresh } from '[@ohos](/user/ohos)/pulltorefresh'
@Entry
@Component
struct ScrollCeilingTab {
scroller: Scroller = new Scroller()
private tabscroller: Scroller = new Scroller();
@State itemData: Array<number> = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
tabTitles: Array<string> = ['Tab1', 'Tab2', 'Tab3']
@Builder
private getListView() {
List({ space: 20, scroller: this.tabscroller }) {
ForEach(this.itemData, (item: number) => {
ListItem() {
Text(`${item}`)
.height(80)
.width('100%')
.textAlign(TextAlign.Center)
.backgroundColor(0xDDDDDD)
.margin({ bottom: 5 })
}
})
}
.nestedScroll({
scrollForward: NestedScrollMode.PARENT_FIRST,
scrollBackward: NestedScrollMode.SELF_FIRST
})
.backgroundColor('#eeeeee')
.divider({ strokeWidth: 1, color: 0x222222 })
.edgeEffect(EdgeEffect.None) // 必须设置列表为滑动到边缘无效果
}
@Builder
tabContentData(tabTitle: string) {
TabContent() {
Column() {
PullToRefresh({
data: this.itemData,
scroller: this.tabscroller,
customList: () => {
this.getListView();
},
// 可选项,下拉刷新回调
onRefresh: () => {
return new Promise<string>((resolve, reject) => {
// 模拟网络请求操作,请求网络2秒后得到数据,通知组件,变更列表数据
setTimeout(() => {
resolve('刷新成功');
let num = this.itemData.length
this.itemData.push(num);
}, 500);
});
},
// 可选项,上拉加载更多回调
onLoadMore: () => {
return new Promise<string>((resolve, reject) => {
setTimeout(() => {
resolve('');
let num = this.itemData.length
this.itemData.push(num);
}, 2000);
});
},
customLoad: null,
customRefresh: null,
})
}
}
.tabBar(tabTitle)
.padding({ top: 5, bottom: 5 })
.borderWidth(1)
.borderColor(Color.Red)
}
build() {
Column({ space: 10 }) {
Scroll(this.scroller) {
Column() {
Image($r('app.media.startIcon')).height(70)
Tabs() {
ForEach(this.tabTitles, (title: string) => {
this.tabContentData(title)
})
}.borderWidth(2)
}.width('90%').alignItems(HorizontalAlign.Center)
}.width('100%').align(Alignment.Center).scrollBar(BarState.Off)
}
}
}
- 场景二解决方案: 可以使用$$双向绑定Refresh组件参数refreshing,点击按钮手动控制组件刷新状态实现,示例代码如下:
@Entry
@Component
struct RefreshExample {
@State isRefreshing: boolean = false
@State arr: String[] = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10']
build() {
Column() {
Row() {
Button('开始刷新')
.onClick(() => {
// 手动开始刷新
this.isRefreshing = true
})
Button('结束刷新')
.onClick(() => {
// 手动停止刷新
this.isRefreshing = false
})
}
Refresh({ refreshing: $$this.isRefreshing }) {
List() {
ForEach(this.arr, (item: string) => {
ListItem() {
Text('' + item)
.width('70%')
.height(80)
.fontSize(16)
.margin(2)
.borderRadius(10)
.backgroundColor(0xFFFFFF)
}
}, (item: string) => item)
}
}
.onRefreshing(() => {
// 2秒后自动结束刷新
setTimeout(() => {
this.isRefreshing = false
}, 2000)
})
.backgroundColor(0x89CFF0)
}
}
}
- 场景三解决方案: 可以在外层List使用Refresh组件实现上拉刷新,内层List使用pulltofresh进行下拉加载,代码示例如下:
import { PullToRefresh, PullToRefreshConfigurator } from '[@ohos](/user/ohos)/pulltorefresh'
const tabsList = ["能源", "化工", "塑料", "化纤", "聚氨酯"]
@Component
struct Index {
private refreshScroller: Scroller = new Scroller();
private refreshConfigurator: PullToRefreshConfigurator = new PullToRefreshConfigurator();
@State list: Array<string> =
["a", "b", "c", "d", "e", "f", "a1", "b1", "c1", "d1", "e1", "f1"]
aboutToAppear(): void {
this.refreshConfigurator.setHasRefresh(false);
}
@Builder
getListView() {
List({ space: 10, scroller: this.refreshScroller }) {
ForEach(this.list,
(item: string, idx: number) => {
ListItem() {
Text(item)
.width("100%")
.height(200)
.width('100%')
.backgroundColor("#ffe0dbdb")
}
})
}
.height('100%')
.width("100%")
.scrollBar(BarState.Off)
.edgeEffect(EdgeEffect.None)
.nestedScroll({
scrollForward: NestedScrollMode.PARENT_FIRST,
scrollBackward: NestedScrollMode.SELF_FIRST
})
}
build() {
Column({ space: 10 }) {
PullToRefresh({
// 必传项,列表组件所绑定的数据
data: $list,
refreshConfigurator: this.refreshConfigurator,
// 必传项,需绑定传入主体布局内的列表或宫格组件
scroller: this.refreshScroller,
// 必传项,自定义主体布局,内部有列表或宫格组件
customList: () => {
// 一个用@Builder修饰过的UI方法
this.getListView();
},
// 可选项,上拉加载更多回调
onLoadMore: () => {
return new Promise<string>((resolve, reject) => {
// 模拟网络请求操作,请求网络2秒后得到数据,通知组件,变更列表数据
setTimeout(() => {
resolve('加载更多啦啦啦');
this.list.push('a2');
this.list.push('b2');
}, 2000);
});
},
customLoad: null,
customRefresh: null,
}).width('100%').height('100%')
}.width('100%')
}
}
@Entry
@Component
struct KeyboadPage {
private refreshScroller: Scroller = new Scroller();
@State isRefresh: boolean = false;
build() {
Column({ space: 10 }) {
Refresh({ refreshing: $$this.isRefresh, builder: this.buildRefreshCom() }) {
this.getListView()
}.onRefreshing(() => {
setTimeout(() => {
this.isRefresh = false;
}, 1000)
})
}
.width('100%')
.height('100%')
}
@Builder
buildRefreshCom() {
Row() {
LoadingProgress().height(32)
Text("正在刷新...").fontSize(16).margin({
left: 20,
})
}
.alignItems(VerticalAlign.Center)
}
@Builder
getListView() {
List({ scroller: this.refreshScroller }) {
ListItem() {
Text("第一条")
.width("100%")
.height(300)
.backgroundColor("#ffe0dbdb")
}
ListItem() {
Text("第二条")
.width("100%")
.height(500)
.backgroundColor("#ff74a6ec")
}
ListItem() {
Text("第三条")
.width("100%")
.height(200)
.backgroundColor("#ffdbdfe0")
}
ListItem() {
Tabs({
barPosition: BarPosition.Start,
}) {
ForEach(tabsList,
(item: string, idx: number) => {
TabContent() {
Index()
}
.tabBar(this.watchingTabBuilder(idx, item))
})
}
.width("100%")
.barHeight(50)
.barMode(BarMode.Scrollable)
.onChange((index: number) => {
})
}
}
.edgeEffect(EdgeEffect.None)
.height('100%')
.width('100%')
.scrollBar(BarState.Off)
}
@Builder
watchingTabBuilder(index: number, item: string) {
Text(item)
.fontSize(16)
.fontColor("#121212")
.padding({
left: 15,
right: 15,
top: 12,
bottom: 12
})
.borderRadius(5)
}
}
【常见FAQ】 Q:页面的数据已经全部加载完成时,如何结束上拉加载的状态?
A:将PullToRefreshConfigurator类的setHasLoadMore属性为false,即关闭上拉加载功能。
Q:除了使用PullToRefresh三方组件,是否还有其他方式实现List下拉刷新?
A:HarmonyOS提供了Refresh组件,该组件是进行页面下拉操作并显示刷新动效的容器组件。
Q:除了使用PullToRefresh三方组件,是否还有其他方式实现List上拉加载?
A:可以通过监听List组件的滚动状态手动实现数据加载的逻辑,示例代码为:
@Entry
@Component
struct Index {
@State nums: number[] = Array(20).fill(Date.now())
isReachEnd: boolean = false
scroller: Scroller = new Scroller()
build() {
List({ scroller: this.scroller }) {
}
// 开始滚动
.onScrollStart(() => {
this.isReachEnd = false
})
// 滚动结束
.onScrollStop(() => {
if (this.isReachEnd) {
setTimeout(() => {
let newNums: number[] = Array(2).fill(Date.now())
this.nums.push(...newNums)
this.scroller.scrollEdge(Edge.End)
promptAction.showToast({
message: '新加载2条'
})
}, 2000)
}
})
// 到达滚动底部
.onReachEnd(() => {
this.isReachEnd = true
})
}
}
更多关于HarmonyOS 鸿蒙Next中如何实现下拉刷新功能的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
在HarmonyOS Next中,下拉刷新功能主要通过Refresh组件实现。该组件是ArkUI提供的标准容器组件,用于为可滚动组件(如List、Scroll)添加下拉刷新交互。
核心实现步骤:
- 使用
Refresh组件包裹你的可滚动列表(如List)。 - 通过
Refresh的onRefresh事件回调来触发数据刷新逻辑。 - 在回调中执行你的数据加载或更新操作。
- 操作完成后,调用
RefreshController的finishRefresh方法结束刷新状态并更新UI。
示例代码结构:
@Entry
@Component
struct Index {
private controller: RefreshController = new RefreshController()
build() {
Refresh({ controller: this.controller, onRefresh: () => {
// 执行你的数据刷新逻辑
// ...
// 刷新完成后
this.controller.finishRefresh()
}}) {
List() {
// 你的列表项内容
}
}
}
}
在HarmonyOS Next中,实现下拉刷新功能主要依赖于Refresh组件。这是一个高度封装的容器组件,可以轻松地为列表(如List、Grid等)添加下拉刷新和上拉加载的能力。
以下是实现下拉刷新的核心步骤和代码示例:
1. 基本使用
将Refresh组件包裹在你的可滚动组件(如List)外部,并通过onRefresh回调触发数据刷新逻辑。
import { Refresh, List, ListItem, Text } from '@kit.ArkUI';
@Entry
@Component
struct RefreshExample {
@State data: string[] = ['Item 1', 'Item 2', 'Item 3'];
@State isRefreshing: boolean = false; // 控制刷新状态
build() {
Column() {
// 使用Refresh组件包裹List
Refresh({
onRefresh: () => {
// 触发刷新时的回调
this.isRefreshing = true;
// 模拟网络请求,2秒后更新数据并结束刷新
setTimeout(() => {
this.data = ['New Item 1', 'New Item 2', 'New Item 3'];
this.isRefreshing = false;
}, 2000);
},
isRefreshing: this.isRefreshing // 绑定刷新状态
}) {
List() {
ForEach(this.data, (item: string) => {
ListItem() {
Text(item)
.fontSize(20)
.margin(10)
}
})
}
.width('100%')
.height('100%')
}
}
.width('100%')
.height('100%')
}
}
2. 关键属性说明
onRefresh: () => void:用户下拉触发刷新时执行的回调函数。你应在此函数中执行数据加载逻辑,并在完成后将isRefreshing设置为false。isRefreshing: boolean:控制刷新状态的变量。设置为true时显示加载指示器,数据加载完成后应设置为false以隐藏指示器。offset: Length(可选):触发onRefresh回调的下拉距离阈值,默认为30vp。
3. 自定义刷新指示器
你可以通过builder属性自定义下拉时显示的UI,实现品牌化的加载动画。
Refresh({
onRefresh: this.handleRefresh,
isRefreshing: this.isRefreshing,
builder: (refreshState: RefreshStatus) => {
// 根据状态自定义显示内容
if (refreshState === RefreshStatus.Inactive) {
return Text('下拉刷新'); // 初始状态
} else if (refreshState === RefreshStatus.Drag) {
return Text('释放立即刷新'); // 下拉中
} else if (refreshState === RefreshStatus.Refresh) {
return Text('正在刷新...'); // 刷新中
} else {
return Text('刷新完成'); // 刷新完成
}
}
}) {
// 你的列表内容
}
4. 配合上拉加载
Refresh组件也支持上拉加载更多功能,通过onLoadMore和isLoadingMore属性实现,用法与下拉刷新类似。
注意事项
- 确保
isRefreshing状态在数据加载完成后及时更新,否则加载指示器会一直显示。 - 列表内容需要有足够的高度才能触发下拉手势,如果数据过少,可以暂时设置一个最小高度。
Refresh组件目前主要适用于List、Grid、Scroll等可滚动组件。
通过上述方式,你可以高效地在HarmonyOS Next应用中集成标准或自定义的下拉刷新交互。

