• Home
Name Date Size #Lines LOC

..--

AppScope/22-Oct-2025-3532

entry/22-Oct-2025-621544

hvigor/22-Oct-2025-2322

pulltorefresh/22-Oct-2025-1,077998

.gitignoreD22-Oct-2025156 1313

README.mdD22-Oct-202510.1 KiB269241

build-profile.json5D22-Oct-20251.4 KiB6160

code-linter.json5D22-Oct-2025958 3534

hvigorfile.tsD22-Oct-2025843 225

oh-package.json5D22-Oct-2025809 2624

ohosTest.mdD22-Oct-20251.2 KiB108

README.md

1# 下拉刷新与上滑加载案例
2
3### 介绍
4
5本示例介绍使用第三方库的PullToRefresh组件实现列表的下拉刷新数据和上滑加载后续数据。
6
7### 效果图预览
8
9<img src="./pulltorefresh/src/main/resources/base/media/pull_to_refresh_news.gif" width="300">
10
11**使用说明**
12
131. 进入页面,下拉列表触发刷新数据事件,等待数据刷新完成。
142. 上滑列表到底部,触发加载更多数据事件,等待数据加载完成。
15
16### 实现思路
17
181. 使用第三方库pullToRefresh组件,将列表组件、绑定的数据对象和scroller对象包含进去,并添加上滑与下拉方法。源码参考[PullToRefreshNews.ets](./pulltorefresh/src/main/ets/pages/PullToRefreshNews.ets)
19    ```typescript
20    PullToRefresh({
21      // 必传项,列表组件所绑定的数据
22      data: $newsData,
23      // 必传项,需绑定传入主体布局内的列表或宫格组件
24      scroller: this.scroller,
25      // 必传项,自定义主体布局,内部有列表或宫格组件
26      customList: () => {
27        // 一个用@Builder修饰过的UI方法
28        this.getListView();
29      },
30      // 下拉刷新回调
31      onRefresh: () => {
32        return new Promise<string>((resolve, reject) => {
33          ...
34        });
35      },
36      // 上滑加载回调
37      onLoadMore: () => {
38        return new Promise<string>((resolve, reject) => {
39          ...
40        });
41      },
42      customLoad: () => this.customLoad(),
43      customRefresh: null,
44    })
45    ```
462. 使用LazyForEach循环渲染列表。源码参考[PullToRefreshNews.ets](./pulltorefresh/src/main/ets/pages/PullToRefreshNews.ets)
47    ```typescript
48    LazyForEach(this.newsData, (item: NewsData) => {
49      ListItem() {
50        ...
51      }
52    });
53    ```
543. 模拟列表总页数,加载完全部信息后提示已经到底部。源码参考[PullToRefreshNews.ets](./pulltorefresh/src/main/ets/pages/PullToRefreshNews.ets)
55   ```javascript
56   onLoadMore: () => {
57     return new Promise<string>((resolve, reject) => {
58       // 模拟数据列表页超过4页后已到达底部,无法继续加载
59       if (this.newsDataListIndex < NEWS_MAX_LIST) {
60         // 模拟网络请求操作,请求网络1.5秒后得到数据,通知组件变更列表数据
61         setTimeout(() => {
62           let newsModelMockData: Array<NewsData> = getNews(MOCK_DATA_FILE_ONE_DIR)
63           for (let j = 0; j < NEWS_MOCK_DATA_COUNT; j++) {
64             this.newsData.pushData(newsModelMockData[j]);
65           }
66           this.newsDataListIndex++;
67           resolve('');
68         }, NEWS_REFRESH_TIME);
69       } else {
70           // 如果已满4页,更改上拉提示信息提示已经加载完所有数据
71           setTimeout(() => {
72           resolve('');
73         }, NEWS_REFRESH_TIME);
74       }
75     });
76   }
77   ```
784. 自定义下拉更新动画。源码参考[PullToRefreshNews.ets](./pulltorefresh/src/main/ets/pages/PullToRefreshNews.ets)
79   ```typescript
80   @Builder
81   private customRefresh() {
82      Row() {
83         // 下滑加载图片
84         Image($r('app.media.pull_icon_load'))
85            .width($r('app.string.pull_refresh_load_width'))
86            .height($r('app.string.pull_refresh_load_height'))
87            .objectFit(ImageFit.Contain)
88            .rotate({
89               z: 1,
90               angle: this.angle2 !== undefined ? this.angle2 : 0
91            })
92            .width(this.refreshConfigurator.getLoadImgHeight())
93            .height(this.refreshConfigurator.getLoadImgHeight())
94
95         // 下拉时提示文本
96         Stack() {
97            Text(CURRENT_DATA_TIP)
98               .height($r('app.string.pull_refresh_load_height'))
99               .textAlign(TextAlign.Center)
100               .margin({ left: this.newsDataListIndex === NEWS_MAX_LIST && this.isLoading ? 0 : 8 })
101               .fontColor(this.refreshConfigurator !== undefined ? this.refreshConfigurator.getLoadTextColor() : 0)
102               .fontSize(this.refreshConfigurator !== undefined ? this.refreshConfigurator.getLoadTextSize() : 0)
103               .visibility(this.pullHeightValue <= CHANGE_PAGE_STATE ? Visibility.Visible : Visibility.Hidden)
104            Text(NEW_DATA_TIP)
105               .height($r('app.string.pull_refresh_load_height'))
106               .textAlign(TextAlign.Center)
107               .margin({ left: this.newsDataListIndex === NEWS_MAX_LIST && this.isLoading ? 0 : 8 })
108               .fontColor(this.refreshConfigurator !== undefined ? this.refreshConfigurator.getLoadTextColor() : 0)
109               .fontSize(this.refreshConfigurator !== undefined ? this.refreshConfigurator.getLoadTextSize() : 0)
110               .visibility(this.pullHeightValue > CHANGE_PAGE_STATE ? Visibility.Visible : Visibility.Hidden)
111         }
112      }
113      .height($r('app.string.pull_refresh_load_height'))
114   }
115   ```
1165. 通过下拉距离判断页面是刷新当前数据还是更新为下一页。源码参考[PullToRefreshNews.ets](./pulltorefresh/src/main/ets/pages/PullToRefreshNews.ets)
117   ```typescript
118   onAnimPullDown: (value) => {
119      this.pullHeightValue = value;
120   },
121   onAnimRefreshing: (value, width, height) => {
122      if (value !== undefined && width !== undefined && height !== undefined) {
123         if (value) {
124            this.angle2 = value * 360;
125            if (this.pullHeightValue > LOAD_PULL_STATE_CHANGE && this.pullHeightValue <= CHANGE_PAGE_STATE) {
126               this.isChangePage = false;
127            } else {
128               // 当下拉到最顶部时,触发更新页面,不再刷新当前页。
129               this.isChangePage = true;
130            }
131         }
132      }
133   }
134   ```
1356. 自定义上拉加载动画。源码参考[PullToRefreshNews.ets](./pulltorefresh/src/main/ets/pages/PullToRefreshNews.ets)
136   ```typescript
137   @Builder
138   private customLoad() {
139     Row() {
140       Stack() {
141         // 上拉1阶段箭头图片
142         Image(pull_icon_up)
143           .width('100%')
144           .height('100%')
145           .objectFit(ImageFit.Contain)
146           .visibility(this.isPullUp ? Visibility.Visible : Visibility.Hidden)
147           .rotate({
148             z: 1,
149             angle: this.angle1 !== undefined ? this.angle1 : 0
150           })
151         // 加载时图片
152         Image(pull_icon_load)
153           .width('100%')
154           .height('100%')
155           .objectFit(ImageFit.Contain)
156           .visibility(this.isLoading ? Visibility.Visible : Visibility.Hidden)
157           .rotate({
158             z: 1,
159             angle: this.angle2 !== undefined ? this.angle2 : 0
160           })
161       }
162       // 最后一页加载时隐藏加载图片
163       .width(this.newsDataListIndex === NEWS_MAX_LIST && this.isLoading ? 0 :
164       this.refreshConfigurator.getLoadImgHeight())
165       .height(this.newsDataListIndex === NEWS_MAX_LIST && this.isLoading ? 0 :
166       this.refreshConfigurator.getLoadImgHeight())
167
168       // 上拉过程与加载时提示文本
169       Text(this.loadText)
170         .height('100%')
171         .textAlign(TextAlign.Center)
172         .margin({ left: this.newsDataListIndex === NEWS_MAX_LIST && this.isLoading ? 0 : 8 })
173         .fontColor(this.refreshConfigurator !== undefined ? this.refreshConfigurator.getLoadTextColor() : 0)
174         .fontSize(this.refreshConfigurator !== undefined ? this.refreshConfigurator.getLoadTextSize() : 0)
175     }
176     .height('100%')
177   }
178   ```
1797. 设置上拉与加载时的动画回调。源码参考[PullToRefreshNews.ets](./pulltorefresh/src/main/ets/pages/PullToRefreshNews.ets)
180   ```typescript
181   onAnimPullUp: (value, width, height) => {
182     if (value !== undefined && width !== undefined && height !== undefined) {
183       if (value) {
184         this.isLoading = false;
185         this.isPullUp = true;
186         // 判断上拉拖拽过程中高度是否超过阶段临界值
187         if (value < LOAD_PULL_STATE_CHANGE) {
188           // 归零角度,保持箭头朝上
189            this.angle1 = 0;
190            // 改变提示文本为上拉1阶段
191            this.loadText = LOAD_TEXT_PULL_UP_1;
192         } else {
193           // 翻转角度,保持箭头朝下
194            this.angle1 = 180;
195            // 改变提示文本为上拉2阶段
196            this.loadText = LOAD_TEXT_PULL_UP_2;
197         }
198       }
199     }
200   },
201
202   onAnimLoading: (value, width, height) => {
203     if (value !== undefined && width !== undefined && height !== undefined) {
204       if (value) {
205         this.isPullUp = false;
206         this.isLoading = true;
207         // 更改角度使加载图片保持旋转
208         this.angle2 = value * 360;
209         // 判读页码是否为最后一页
210         if (this.newsDataListIndex !== NEWS_MAX_LIST) {
211           this.loadText = LOAD_DEFAULT_TEXT;
212         } else {
213           // 最后一页更换文本提示已经到底了
214           this.loadText = LOAD_STOP_TEXT;
215         }
216       }
217     }
218   }
219   ```
220### 高性能知识点
221
222不涉及
223
224### 工程结构&模块类型
225   ```
226   pulltorefreshnews                     // har类型
227   |---model
228   |---|---AppInfo.ets                   // App信息
229   |---|---UserInformation.ets           // 用户信息
230   |---view
231   |---|---PullToRefreshNews.ets         // 视图层-场景列表页面
232   ```
233
234### 模块依赖
235
236[PullToRefresh](https://gitee.com/openharmony-sig/PullToRefresh)
237
238### 参考资料
239
240[PullToRefresh](https://gitee.com/openharmony-sig/PullToRefresh)
241[LazyForEach](https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/reference/apis-arkui/arkui-ts/ts-rendering-control-lazyforeach.md)
242[PullToRefresh第三方库](https://ohpm.openharmony.cn/#/cn/detail/@ohos%2Fpulltorefresh)
243
244### 相关权限
245
246不涉及。
247
248### 依赖
249
250不涉及。
251
252### 约束与限制
253
2541.本示例仅支持标准系统上运行,支持设备:RK3586。
255
2562.本示例为Stage模型,支持API12版本SDK,SDK版本号(API Version 12 Release)。
257
2583.本示例需要使用DevEco Studio版本号(DevEco Studio 5.0.0 Release)及以上版本才可编译运行。
259
260### 下载
261
262如需单独下载本工程,执行如下命令:
263
264```shell
265git init
266git config core.sparsecheckout true
267echo code/UI/PullToRefresh/ > .git/info/sparse-checkout
268git remote add origin https://gitee.com/openharmony/applications_app_samples.git
269git pull origin master