下拉刷新与上滑加载案例
介绍
本示例介绍使用第三方库的PullToRefresh组件实现列表的下拉刷新数据和上滑加载后续数据。
效果图预览

使用说明
- 进入页面,下拉列表触发刷新数据事件,等待数据刷新完成。
- 上滑列表到底部,触发加载更多数据事件,等待数据加载完成。
实现思路
- 使用第三方库pullToRefresh组件,将列表组件、绑定的数据对象和scroller对象包含进去,并添加上滑与下拉方法。源码参考PullToRefreshNews.ets
PullToRefresh({ // 必传项,列表组件所绑定的数据 data: $newsData, // 必传项,需绑定传入主体布局内的列表或宫格组件 scroller: this.scroller, // 必传项,自定义主体布局,内部有列表或宫格组件 customList: () => { // 一个用@Builder修饰过的UI方法 this.getListView(); }, // 下拉刷新回调 onRefresh: () => { return new Promise<string>((resolve, reject) => { ... }); }, // 上滑加载回调 onLoadMore: () => { return new Promise<string>((resolve, reject) => { ... }); }, customLoad: () => this.customLoad(), customRefresh: null, }) - 使用LazyForEach循环渲染列表。源码参考PullToRefreshNews.ets
LazyForEach(this.newsData, (item: NewsData) => { ListItem() { ... } }); - 模拟列表总页数,加载完全部信息后提示已经到底部。源码参考PullToRefreshNews.ets
onLoadMore: () => { return new Promise<string>((resolve, reject) => { // 模拟数据列表页超过4页后已到达底部,无法继续加载 if (this.newsDataListIndex < NEWS_MAX_LIST) { // 模拟网络请求操作,请求网络1.5秒后得到数据,通知组件变更列表数据 setTimeout(() => { let newsModelMockData: Array<NewsData> = getNews(MOCK_DATA_FILE_ONE_DIR) for (let j = 0; j < NEWS_MOCK_DATA_COUNT; j++) { this.newsData.pushData(newsModelMockData[j]); } this.newsDataListIndex++; resolve(''); }, NEWS_REFRESH_TIME); } else { // 如果已满4页,更改上拉提示信息提示已经加载完所有数据 setTimeout(() => { resolve(''); }, NEWS_REFRESH_TIME); } }); } - 自定义下拉更新动画。源码参考PullToRefreshNews.ets
@Builder private customRefresh() { Row() { // 下滑加载图片 Image($r('app.media.pull_icon_load')) .width($r('app.string.pull_refresh_load_width')) .height($r('app.string.pull_refresh_load_height')) .objectFit(ImageFit.Contain) .rotate({ z: 1, angle: this.angle2 !== undefined ? this.angle2 : 0 }) .width(this.refreshConfigurator.getLoadImgHeight()) .height(this.refreshConfigurator.getLoadImgHeight()) // 下拉时提示文本 Stack() { Text(CURRENT_DATA_TIP) .height($r('app.string.pull_refresh_load_height')) .textAlign(TextAlign.Center) .margin({ left: this.newsDataListIndex === NEWS_MAX_LIST && this.isLoading ? 0 : 8 }) .fontColor(this.refreshConfigurator !== undefined ? this.refreshConfigurator.getLoadTextColor() : 0) .fontSize(this.refreshConfigurator !== undefined ? this.refreshConfigurator.getLoadTextSize() : 0) .visibility(this.pullHeightValue <= CHANGE_PAGE_STATE ? Visibility.Visible : Visibility.Hidden) Text(NEW_DATA_TIP) .height($r('app.string.pull_refresh_load_height')) .textAlign(TextAlign.Center) .margin({ left: this.newsDataListIndex === NEWS_MAX_LIST && this.isLoading ? 0 : 8 }) .fontColor(this.refreshConfigurator !== undefined ? this.refreshConfigurator.getLoadTextColor() : 0) .fontSize(this.refreshConfigurator !== undefined ? this.refreshConfigurator.getLoadTextSize() : 0) .visibility(this.pullHeightValue > CHANGE_PAGE_STATE ? Visibility.Visible : Visibility.Hidden) } } .height($r('app.string.pull_refresh_load_height')) } - 通过下拉距离判断页面是刷新当前数据还是更新为下一页。源码参考PullToRefreshNews.ets
onAnimPullDown: (value) => { this.pullHeightValue = value; }, onAnimRefreshing: (value, width, height) => { if (value !== undefined && width !== undefined && height !== undefined) { if (value) { this.angle2 = value * 360; if (this.pullHeightValue > LOAD_PULL_STATE_CHANGE && this.pullHeightValue <= CHANGE_PAGE_STATE) { this.isChangePage = false; } else { // 当下拉到最顶部时,触发更新页面,不再刷新当前页。 this.isChangePage = true; } } } } - 自定义上拉加载动画。源码参考PullToRefreshNews.ets
@Builder private customLoad() { Row() { Stack() { // 上拉1阶段箭头图片 Image(pull_icon_up) .width('100%') .height('100%') .objectFit(ImageFit.Contain) .visibility(this.isPullUp ? Visibility.Visible : Visibility.Hidden) .rotate({ z: 1, angle: this.angle1 !== undefined ? this.angle1 : 0 }) // 加载时图片 Image(pull_icon_load) .width('100%') .height('100%') .objectFit(ImageFit.Contain) .visibility(this.isLoading ? Visibility.Visible : Visibility.Hidden) .rotate({ z: 1, angle: this.angle2 !== undefined ? this.angle2 : 0 }) } // 最后一页加载时隐藏加载图片 .width(this.newsDataListIndex === NEWS_MAX_LIST && this.isLoading ? 0 : this.refreshConfigurator.getLoadImgHeight()) .height(this.newsDataListIndex === NEWS_MAX_LIST && this.isLoading ? 0 : this.refreshConfigurator.getLoadImgHeight()) // 上拉过程与加载时提示文本 Text(this.loadText) .height('100%') .textAlign(TextAlign.Center) .margin({ left: this.newsDataListIndex === NEWS_MAX_LIST && this.isLoading ? 0 : 8 }) .fontColor(this.refreshConfigurator !== undefined ? this.refreshConfigurator.getLoadTextColor() : 0) .fontSize(this.refreshConfigurator !== undefined ? this.refreshConfigurator.getLoadTextSize() : 0) } .height('100%') } - 设置上拉与加载时的动画回调。源码参考PullToRefreshNews.ets
onAnimPullUp: (value, width, height) => { if (value !== undefined && width !== undefined && height !== undefined) { if (value) { this.isLoading = false; this.isPullUp = true; // 判断上拉拖拽过程中高度是否超过阶段临界值 if (value < LOAD_PULL_STATE_CHANGE) { // 归零角度,保持箭头朝上 this.angle1 = 0; // 改变提示文本为上拉1阶段 this.loadText = LOAD_TEXT_PULL_UP_1; } else { // 翻转角度,保持箭头朝下 this.angle1 = 180; // 改变提示文本为上拉2阶段 this.loadText = LOAD_TEXT_PULL_UP_2; } } } }, onAnimLoading: (value, width, height) => { if (value !== undefined && width !== undefined && height !== undefined) { if (value) { this.isPullUp = false; this.isLoading = true; // 更改角度使加载图片保持旋转 this.angle2 = value * 360; // 判读页码是否为最后一页 if (this.newsDataListIndex !== NEWS_MAX_LIST) { this.loadText = LOAD_DEFAULT_TEXT; } else { // 最后一页更换文本提示已经到底了 this.loadText = LOAD_STOP_TEXT; } } } }
高性能知识点
不涉及
工程结构&模块类型
pulltorefreshnews // har类型
|---model
|---|---AppInfo.ets // App信息
|---|---UserInformation.ets // 用户信息
|---view
|---|---PullToRefreshNews.ets // 视图层-场景列表页面
模块依赖
参考资料
PullToRefresh LazyForEach PullToRefresh第三方库
相关权限
不涉及。
依赖
不涉及。
约束与限制
1.本示例仅支持标准系统上运行,支持设备:RK3586。
2.本示例为Stage模型,支持API12版本SDK,SDK版本号(API Version 12 Release)。
3.本示例需要使用DevEco Studio版本号(DevEco Studio 5.0.0 Release)及以上版本才可编译运行。
下载
如需单独下载本工程,执行如下命令:
git init
git config core.sparsecheckout true
echo code/UI/PullToRefresh/ > .git/info/sparse-checkout
git remote add origin https://gitee.com/openharmony/applications_app_samples.git
git pull origin master