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