• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Refresh
2
3<!--Kit: ArkUI-->
4<!--Subsystem: ArkUI-->
5<!--Owner: @yylong-->
6<!--Designer: @yylong-->
7<!--Tester: @liuzhenshuo-->
8<!--Adviser: @HelloCrease-->
9
10 可以进行页面下拉操作并显示刷新动效的容器组件。
11
12>  **说明:**
13>
14>  - 该组件从API version 8开始支持。后续版本如有新增内容,则采用上角标单独标记该内容的起始版本。
15>
16>  - 该组件从API version 12开始支持与垂直滚动的Swiper和Web的联动。当Swiper设置loop属性为true时,Refresh无法和Swiper产生联动。
17>
18>  - Refresh和内容大小小于组件自身的List组件嵌套使用并且中间还有其他组件时,手势可能会被中间组件响应,导致Refresh未产生下拉刷新效果,可以将[alwaysEnabled](./ts-container-scrollable-common.md#edgeeffectoptions11对象说明)参数设为true,此时List会响应手势并通过嵌套滚动带动Refresh组件产生下拉刷新效果,具体可以参考[示例9不满一屏实现下拉刷新](#示例9不满一屏场景实现下拉刷新)。
19>
20>  - 组件内部已绑定手势实现跟手滚动等功能,需要增加自定义手势操作时请参考[手势拦截增强](ts-gesture-blocking-enhancement.md)进行处理。
21>
22>  - 组件无法通过鼠标按下拖动操作进行下拉刷新。
23
24## 子组件
25
26支持单个子组件。
27
28从API version 11开始,Refresh子组件会跟随手势下拉而下移。
29
30## 接口
31
32Refresh(value: RefreshOptions)
33
34创建Refresh容器。
35
36**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。
37
38**系统能力:** SystemCapability.ArkUI.ArkUI.Full
39
40**参数:**
41
42| 参数名 | 类型 | 必填 | 说明 |
43| -------- | -------- | -------- | -------- |
44| value |  [RefreshOptions](#refreshoptions对象说明)| 是 | 刷新组件参数。 |
45
46## RefreshOptions对象说明
47
48用于设置Refresh组件参数。
49
50**系统能力:** SystemCapability.ArkUI.ArkUI.Full
51
52| 名称         | 类型                                      | 只读   | 可选 | 说明                                     |
53| ---------- | ---------------------------------------- | ---- | -- | ---------------------------------------- |
54| refreshing | boolean                                  | 否    | 否 | 组件当前是否处于刷新中状态。true表示处于刷新中状态,false表示未处于刷新中状态。<br/>默认值:false<br/>该参数支持[$$](../../../ui/state-management/arkts-two-way-sync.md)双向绑定变量。 <br/>**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。|
55| offset<sup>(deprecated)</sup>    | number&nbsp;\|&nbsp;string   | 否    | 是 | 下拉起点距离组件顶部的距离。<br/>默认值:16,单位vp。类型为string时,需要显式指定像素单位,如'10px';未指定像素单位时,如'10',单位为vp。 <br/>从API version 11开始废弃,无替代接口。<br/>**说明:**<br/>offset取值范围[0vp,64vp]。大于64vp按照64vp处理。不支持百分比,不支持负数。|
56| friction<sup>(deprecated)</sup>   | number&nbsp;\|&nbsp;string               | 否    | 是 | 下拉摩擦系数,取值范围为0到100。<br/>默认值:62<br/>-&nbsp;0表示下拉刷新容器不跟随手势下拉而下拉。<br/>-&nbsp;100表示下拉刷新容器紧紧跟随手势下拉而下拉。<br/>-&nbsp;数值越大,下拉刷新容器跟随手势下拉的反应越灵敏。<br/>从API version 11开始废弃,从API version 12开始,可用[pullDownRatio](#pulldownratio12)属性替代。 |
57| builder<sup>10+</sup>    | [CustomBuilder](ts-types.md#custombuilder8) | 否    | 是 | 自定义刷新区域显示内容。<br/>**说明:**<br/>API version 10及之前版本,自定义组件的高度限制在64vp之内。API version 11及以后版本没有此限制。 <br/>自定义组件设置了固定高度时,自定义组件会以固定高度显示在刷新区域下方;自定义组件未设置高度时,自定义组件高度会自适应刷新区域高度,会发生自定义组件高度跟随刷新区域变化至0的现象。建议对自定义组件设置最小高度约束来避免自定义组件高度小于预期的情况发生,具体可参照[示例3](#示例3自定义刷新区域显示内容-builder)。 <br/>从API version 12开始,建议使用refreshingContent参数替代builder参数自定义刷新区域显示内容,以避免刷新过程中因自定义组件销毁重建造成的动画中断问题。<br/>**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。|
58| promptText<sup>12+</sup> | [ResourceStr](ts-types.md#resourcestr) | 否 | 是 | 设置刷新区域底部显示的自定义文本。<br/>**说明:**<br/>输入文本的限制参考Text组件,使用builder或refreshingContent参数自定义刷新区域显示内容时,promptText不显示。<br/>promptText设置有效时,[refreshOffset](#refreshoffset12)属性默认值为96vp。<br/>自定义文本最大的字体缩放倍数[maxFontScale](ts-basic-components-text.md#maxfontscale12)为2。<br/>**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。|
59| refreshingContent<sup>12+</sup>    | [ComponentContent](../js-apis-arkui-ComponentContent.md) | 否    | 是 | 自定义刷新区域显示内容。<br/>**说明:**<br/>与builder参数同时设置时builder参数不生效。<br/>自定义组件设置了固定高度时,自定义组件会以固定高度显示在刷新区域下方;自定义组件未设置高度时,自定义组件高度会自适应刷新区域高度,会发生自定义组件高度跟随刷新区域变化至0的现象。建议对自定义组件设置最小高度约束来避免自定义组件高度小于预期的情况发生,具体可参照[示例4](#示例4自定义刷新区域显示内容-refreshingcontent)。 <br/>**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。|
60
61>  **补充说明:**
62>  - 当未设置builder或refreshingContent时,是通过更新子组件的[translate](ts-universal-attributes-transformation.md#translate)属性实现的下拉位移效果,下拉位移过程中不会触发子组件的[onAreaChange](ts-universal-component-area-change-event.md#onareachange)事件,子组件设置[translate](ts-universal-attributes-transformation.md#translate)属性时不会生效。
63>  - 当设置了builder或refreshingContent时,是通过更新子组件相对于Refresh组件的位置实现的下拉位移效果,下拉位移过程中可以触发子组件的[onAreaChange](ts-universal-component-area-change-event.md#onareachange)事件,子组件设置[position](ts-universal-attributes-location.md#position)属性时会固定子组件相对于Refresh组件的位置导致子组件不会跟手进行下拉位移。
64>  - 通过builder参数设置的自定义组件在未指定宽度和高度时,其尺寸将自适应子组件,在指定宽度而未指定高度时,其高度将自适应下拉距离。通过refreshingContent参数设置的自定义组件若未指定高度,其高度同样会自适应下拉距离。当自定义组件高度自适应下拉距离时,随着下拉距离的增加,该组件的高度亦随之增加;当自定义组件的高度设定为固定值或达到最大高度限制时,随着下拉距离的增加,自定义组件与Refresh组件上边界之间的间距亦会随之增加。
65
66## 属性
67
68支持[通用属性](ts-component-general-attributes.md)外,还支持以下属性:
69
70### refreshOffset<sup>12+</sup>
71
72refreshOffset(value: number)
73
74设置触发刷新的下拉偏移量,当下拉距离小于该属性设置值时离手不会触发刷新。
75
76**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
77
78**系统能力:** SystemCapability.ArkUI.ArkUI.Full
79
80**参数:**
81
82| 参数名 | 类型                                        | 必填 | 说明                                                       |
83| ------ | ------------------------------------------- | ---- | ---------------------------------------------------------- |
84| value  | number |  是 | 下拉偏移量,单位vp。<br/>默认值:未设置[promptText](#refreshoptions对象说明)参数时为64vp,设置了[promptText](#refreshoptions对象说明)参数时为96vp。 <br/>如果取值为0或负数的时候此接口采用默认值。|
85
86### pullToRefresh<sup>12+</sup>
87
88pullToRefresh(value: boolean)
89
90设置当下拉距离超过[refreshOffset](#refreshoffset12)时是否能触发刷新。
91
92**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
93
94**系统能力:** SystemCapability.ArkUI.ArkUI.Full
95
96**参数:**
97
98| 参数名 | 类型                                        | 必填 | 说明                                                       |
99| ------ | ------------------------------------------- | ---- | ---------------------------------------------------------- |
100| value  | boolean |  是 | 当下拉距离超过[refreshOffset](#refreshoffset12)时是否能触发刷新。true表示能触发刷新,false表示不能触发刷新。<br/>默认值:true |
101
102### pullDownRatio<sup>12+</sup>
103
104pullDownRatio(ratio: [Optional](ts-universal-attributes-custom-property.md#optionalt12)\<number>)
105
106设置下拉跟手系数。
107
108**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
109
110**系统能力:** SystemCapability.ArkUI.ArkUI.Full
111
112**参数:**
113
114| 参数名 | 类型                                        | 必填 | 说明                                                       |
115| ------ | ------------------------------------------- | ---- | ---------------------------------------------------------- |
116| ratio  | [Optional](ts-universal-attributes-custom-property.md#optionalt12)\<number> |  是 | 下拉跟手系数。数值越大,跟随手势下拉的反应越灵敏。0表示不跟随手势下拉,1表示等比例跟随手势下拉。<br/>没有设置或设置为undefined时,默认使用动态下拉跟手系数,下拉距离越大,跟手系数越小。<br/>有效值为0-1之间的值,小于0的值会被视为0,大于1的值会被视为1。
117
118### maxPullDownDistance<sup>20+</sup>
119
120maxPullDownDistance(distance: Optional\<number>)
121
122设置最大下拉距离。
123
124**原子化服务API:** 从API version 20开始,该接口支持在原子化服务中使用。
125
126**系统能力:** SystemCapability.ArkUI.ArkUI.Full
127
128**参数:**
129
130| 参数名 | 类型                                        | 必填 | 说明                                                       |
131| ------ | ------------------------------------------- | ---- | ---------------------------------------------------------- |
132| distance  | [Optional](ts-universal-attributes-custom-property.md#optionalt12)\<number> |  是 | 最大下拉距离。最大下拉距离的最小值为0,小于0按0处理。当该值小于刷新的下拉偏移量refreshOffset时,Refresh下拉离手不会触发刷新。<br/>undefined和null按没有设置此属性处理。<br/>默认值:undefined<br/>单位:vp
133
134## 事件
135
136除支持[通用事件](ts-component-general-events.md)外,还支持以下事件:
137
138### onStateChange
139
140onStateChange(callback: (state: RefreshStatus) => void)
141
142当前刷新状态变更时,触发回调。
143
144**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。
145
146**系统能力:** SystemCapability.ArkUI.ArkUI.Full
147
148**参数:**
149
150| 参数名 | 类型                                    | 必填 | 说明       |
151| ------ | --------------------------------------- | ---- | ---------- |
152| state  | [RefreshStatus](#refreshstatus枚举说明) | 是   | 刷新状态。 |
153
154### onRefreshing
155
156onRefreshing(callback: () => void)
157
158进入刷新状态时触发回调。
159
160**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。
161
162**系统能力:** SystemCapability.ArkUI.ArkUI.Full
163
164**参数:**
165
166| 参数名 | 类型 | 必填 | 说明 |
167| ------ | ------ | ------ | ------|
168| callback | () => void | 是 | 进入刷新状态时触发的回调。 |
169
170### onOffsetChange<sup>12+</sup>
171
172onOffsetChange(callback: Callback\<number>)
173
174下拉距离发生变化时触发回调。
175
176**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
177
178**系统能力:** SystemCapability.ArkUI.ArkUI.Full
179
180**参数:**
181
182| 参数名 | 类型                                    | 必填 | 说明       |
183| ------ | --------------------------------------- | ---- | ---------- |
184| callback  | Callback\<number> | 是   | 下拉距离。<br/>单位:vp |
185
186
187## RefreshStatus枚举说明
188
189RefreshStatus刷新状态枚举。
190
191**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。
192
193**系统能力:** SystemCapability.ArkUI.ArkUI.Full
194
195| 名称       | 值       | 说明                 |
196| -------- | -------- | -------------------- |
197| Inactive | 0 | 默认未下拉状态。             |
198| Drag     | 1 | 下拉中,下拉距离小于刷新距离。<br/>若此时松手,组件进入Inactive状态;若此时继续下拉使下拉距离超过刷新距离,组件进入OverDrag状态。   |
199| OverDrag | 2 | 下拉中,下拉距离超过刷新距离。<br/>若此时松手,组件进入Refresh状态;若此时上滑使下拉距离小于刷新距离,组件进入Drag状态。      |
200| Refresh  | 3 | 下拉结束,回弹至刷新距离,进入刷新中状态。 |
201| Done     | 4 | 刷新结束,返回初始状态(顶部)。     |
202
203
204## 示例
205
206### 示例1(默认刷新样式)
207
208刷新区域使用默认刷新样式。
209
210```ts
211// xxx.ets
212@Entry
213@Component
214struct RefreshExample {
215  @State isRefreshing: boolean = false;
216  @State arr: String[] = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10'];
217
218  build() {
219    Column() {
220      Row() {
221        Button('开始刷新').onClick(() => {
222          this.isRefreshing = true;
223        })
224        Button('停止刷新').onClick(() => {
225          this.isRefreshing = false;
226        })
227      }
228
229      Refresh({ refreshing: $$this.isRefreshing }) {
230        List() {
231          ForEach(this.arr, (item: string) => {
232            ListItem() {
233              Text('' + item)
234                .width('70%')
235                .height(80)
236                .fontSize(16)
237                .margin(10)
238                .textAlign(TextAlign.Center)
239                .borderRadius(10)
240                .backgroundColor(0xFFFFFF)
241            }
242          }, (item: string) => item)
243        }
244        .onScrollIndex((first: number) => {
245          console.info(first.toString());
246        })
247        .width('100%')
248        .height('100%')
249        .alignListItem(ListItemAlign.Center)
250        .scrollBar(BarState.Off)
251      }
252      .onStateChange((refreshStatus: RefreshStatus) => {
253        console.info('Refresh onStatueChange state is ' + refreshStatus);
254      })
255      .onOffsetChange((value: number) => {
256        console.info('Refresh onOffsetChange offset:' + value);
257      })
258      .onRefreshing(() => {
259        setTimeout(() => {
260          this.isRefreshing = false;
261        }, 2000)
262        console.log('onRefreshing test');
263      })
264      .backgroundColor(0x89CFF0)
265      .refreshOffset(64)
266      .pullToRefresh(true)
267    }
268  }
269}
270```
271
272![zh-cn_image_refresh_default](figures/zh-cn_image_refresh_default.gif)
273
274### 示例2(设置刷新区域显示文本)
275
276通过[promptText](#refreshoptions对象说明)参数设置刷新区域显示文本。
277
278```ts
279// xxx.ets
280@Entry
281@Component
282struct RefreshExample {
283  @State isRefreshing: boolean = false;
284  @State promptText: string = "Refreshing...";
285  @State arr: string[] = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10'];
286
287  build() {
288    Column() {
289      Refresh({ refreshing: $$this.isRefreshing, promptText: this.promptText }) {
290        List() {
291          ForEach(this.arr, (item: string) => {
292            ListItem() {
293              Text(item)
294                .width('70%')
295                .height(80)
296                .fontSize(16)
297                .margin(10)
298                .textAlign(TextAlign.Center)
299                .borderRadius(10)
300                .backgroundColor(0xFFFFFF)
301            }
302          }, (item: string) => item)
303        }
304        .onScrollIndex((first: number) => {
305          console.info(first.toString());
306        })
307        .width('100%')
308        .height('100%')
309        .alignListItem(ListItemAlign.Center)
310        .scrollBar(BarState.Off)
311      }
312      .backgroundColor(0x89CFF0)
313      .pullToRefresh(true)
314      .refreshOffset(96)
315      .onStateChange((refreshStatus: RefreshStatus) => {
316        console.info('Refresh onStatueChange state is ' + refreshStatus);
317      })
318      .onOffsetChange((value: number) => {
319        console.info('Refresh onOffsetChange offset:' + value);
320      })
321      .onRefreshing(() => {
322        setTimeout(() => {
323          this.isRefreshing = false;
324        }, 2000)
325        console.log('onRefreshing test');
326      })
327    }
328  }
329}
330```
331
332![zh-cn_image_refresh_prompttext](figures/zh-cn_image_refresh_prompttext.gif)
333
334### 示例3(自定义刷新区域显示内容-builder)
335
336通过[builder](#refreshoptions对象说明)参数自定义刷新区域显示内容。
337
338```ts
339// xxx.ets
340@Entry
341@Component
342struct RefreshExample {
343  @State isRefreshing: boolean = false;
344  @State arr: String[] = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10'];
345
346  @Builder
347  customRefreshComponent() {
348    Stack() {
349      Row() {
350        LoadingProgress().height(32)
351        Text("Refreshing...").fontSize(16).margin({ left: 20 })
352      }
353      .alignItems(VerticalAlign.Center)
354    }
355    .align(Alignment.Center)
356    .clip(true)
357    // 设置最小高度约束保证自定义组件高度随刷新区域高度变化时自定义组件高度不会低于minHeight。
358    .constraintSize({ minHeight: 32 })
359    .width("100%")
360  }
361
362  build() {
363    Column() {
364      Refresh({ refreshing: $$this.isRefreshing, builder: this.customRefreshComponent() }) {
365        List() {
366          ForEach(this.arr, (item: string) => {
367            ListItem() {
368              Text('' + item)
369                .width('70%')
370                .height(80)
371                .fontSize(16)
372                .margin(10)
373                .textAlign(TextAlign.Center)
374                .borderRadius(10)
375                .backgroundColor(0xFFFFFF)
376            }
377          }, (item: string) => item)
378        }
379        .onScrollIndex((first: number) => {
380          console.info(first.toString());
381        })
382        .width('100%')
383        .height('100%')
384        .alignListItem(ListItemAlign.Center)
385        .scrollBar(BarState.Off)
386      }
387      .backgroundColor(0x89CFF0)
388      .pullToRefresh(true)
389      .refreshOffset(64)
390      .onStateChange((refreshStatus: RefreshStatus) => {
391        console.info('Refresh onStatueChange state is ' + refreshStatus);
392      })
393      .onRefreshing(() => {
394        setTimeout(() => {
395          this.isRefreshing = false;
396        }, 2000)
397        console.log('onRefreshing test');
398      })
399    }
400  }
401}
402```
403
404![zh-cn_image_refresh_builder](figures/zh-cn_image_refresh_builder.gif)
405
406### 示例4(自定义刷新区域显示内容-refreshingContent)
407
408通过[refreshingContent](#refreshoptions对象说明)参数自定义刷新区域显示内容。
409
410```ts
411// xxx.ets
412import { ComponentContent } from '@kit.ArkUI';
413
414class Params {
415  refreshStatus: RefreshStatus = RefreshStatus.Inactive;
416
417  constructor(refreshStatus: RefreshStatus) {
418    this.refreshStatus = refreshStatus;
419  }
420}
421
422@Builder
423function customRefreshingContent(params: Params) {
424  Stack() {
425    Row() {
426      LoadingProgress().height(32)
427      Text("refreshStatus: " + params.refreshStatus).fontSize(16).margin({ left: 20 })
428    }
429    .alignItems(VerticalAlign.Center)
430  }
431  .align(Alignment.Center)
432  .clip(true)
433  // 设置最小高度约束保证自定义组件高度随刷新区域高度变化时自定义组件高度不会低于minHeight。
434  .constraintSize({ minHeight: 32 })
435  .width("100%")
436}
437
438@Entry
439@Component
440struct RefreshExample {
441  @State isRefreshing: boolean = false;
442  @State arr: String[] = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10'];
443  @State refreshStatus: RefreshStatus = RefreshStatus.Inactive;
444  private contentNode?: ComponentContent<Object> = undefined;
445  private params: Params = new Params(RefreshStatus.Inactive);
446
447  aboutToAppear(): void {
448    let uiContext = this.getUIContext();
449    this.contentNode = new ComponentContent(uiContext, wrapBuilder(customRefreshingContent), this.params);
450  }
451
452  build() {
453    Column() {
454      Refresh({ refreshing: $$this.isRefreshing, refreshingContent: this.contentNode }) {
455        List() {
456          ForEach(this.arr, (item: string) => {
457            ListItem() {
458              Text('' + item)
459                .width('70%')
460                .height(80)
461                .fontSize(16)
462                .margin(10)
463                .textAlign(TextAlign.Center)
464                .borderRadius(10)
465                .backgroundColor(0xFFFFFF)
466            }
467          }, (item: string) => item)
468        }
469        .onScrollIndex((first: number) => {
470          console.info(first.toString());
471        })
472        .width('100%')
473        .height('100%')
474        .alignListItem(ListItemAlign.Center)
475        .scrollBar(BarState.Off)
476      }
477      .backgroundColor(0x89CFF0)
478      .pullToRefresh(true)
479      .refreshOffset(96)
480      .onStateChange((refreshStatus: RefreshStatus) => {
481        this.refreshStatus = refreshStatus;
482        this.params.refreshStatus = refreshStatus;
483        // 更新自定义组件内容。
484        this.contentNode?.update(this.params);
485        console.info('Refresh onStatueChange state is ' + refreshStatus);
486      })
487      .onRefreshing(() => {
488        setTimeout(() => {
489          this.isRefreshing = false;
490        }, 2000)
491        console.log('onRefreshing test');
492      })
493    }
494  }
495}
496```
497![zh-cn_image_refresh_refreshingcontent](figures/zh-cn_image_refresh_refreshingcontent.gif)
498
499### 示例5(实现最大下拉距离)
500
501通过[pullDownRatio](#pulldownratio12)属性和[onOffsetChange](#onoffsetchange12)事件实现最大下拉距离。
502
503```ts
504// xxx.ets
505import { ComponentContent } from '@kit.ArkUI';
506
507@Builder
508function customRefreshingContent() {
509  Stack() {
510    Row() {
511      LoadingProgress().height(32)
512    }
513    .alignItems(VerticalAlign.Center)
514  }
515  .align(Alignment.Center)
516  .clip(true)
517  // 设置最小高度约束保证自定义组件高度随刷新区域高度变化时自定义组件高度不会低于minHeight。
518  .constraintSize({ minHeight: 32 })
519  .width("100%")
520}
521
522@Entry
523@Component
524struct RefreshExample {
525  @State isRefreshing: boolean = false;
526  @State arr: String[] = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10'];
527  @State maxRefreshingHeight: number = 100.0;
528  @State ratio: number = 1;
529  private contentNode?: ComponentContent<Object> = undefined;
530
531  aboutToAppear(): void {
532    let uiContext = this.getUIContext();
533    this.contentNode = new ComponentContent(uiContext, wrapBuilder(customRefreshingContent));
534  }
535
536  build() {
537    Column() {
538      Refresh({ refreshing: $$this.isRefreshing, refreshingContent: this.contentNode }) {
539        List() {
540          ForEach(this.arr, (item: string) => {
541            ListItem() {
542              Text('' + item)
543                .width('70%')
544                .height(80)
545                .fontSize(16)
546                .margin(10)
547                .textAlign(TextAlign.Center)
548                .borderRadius(10)
549                .backgroundColor(0xFFFFFF)
550            }
551          }, (item: string) => item)
552        }
553        .onScrollIndex((first: number) => {
554          console.info(first.toString());
555        })
556        .width('100%')
557        .height('100%')
558        .alignListItem(ListItemAlign.Center)
559        .scrollBar(BarState.Off)
560      }
561      .backgroundColor(0x89CFF0)
562      .pullDownRatio(this.ratio)
563      .pullToRefresh(true)
564      .refreshOffset(64)
565      .onOffsetChange((offset: number) => {
566        // 越接近最大距离,下拉跟手系数越小。
567        this.ratio = 1 - Math.pow((offset / this.maxRefreshingHeight), 3);
568      })
569      .onStateChange((refreshStatus: RefreshStatus) => {
570        console.info('Refresh onStatueChange state is ' + refreshStatus);
571      })
572      .onRefreshing(() => {
573        setTimeout(() => {
574          this.isRefreshing = false;
575        }, 2000)
576        console.log('onRefreshing test');
577      })
578    }
579  }
580}
581```
582
583![zh-cn_image_refresh_maxrefreshingheight](figures/zh-cn_image_refresh_maxrefreshingheight.gif)
584
585### 示例6(实现下拉刷新上拉加载更多)
586
587Refresh组件与[List](ts-container-list.md)组件组合实现下拉刷新上拉加载更多效果。
588
589```ts
590// xxx.ets
591@Entry
592@Component
593struct ListRefreshLoad {
594  @State arr: Array<number> = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
595  @State refreshing: boolean = false;
596  @State refreshOffset: number = 0;
597  @State refreshState: RefreshStatus = RefreshStatus.Inactive;
598  @State isLoading: boolean = false;
599
600  @Builder
601  refreshBuilder() {
602    Stack({ alignContent: Alignment.Bottom }) {
603      // 可以通过刷新状态控制是否存在Progress组件。
604      // 当刷新状态处于下拉中或刷新中状态时Progress组件才存在。
605      if (this.refreshState != RefreshStatus.Inactive && this.refreshState != RefreshStatus.Done) {
606        Progress({ value: this.refreshOffset, total: 64, type: ProgressType.Ring })
607          .width(32).height(32)
608          .style({ status: this.refreshing ? ProgressStatus.LOADING : ProgressStatus.PROGRESSING })
609          .margin(10)
610      }
611    }
612    .clip(true)
613    .height("100%")
614    .width("100%")
615  }
616
617  @Builder
618  footer() {
619    Row() {
620      LoadingProgress().height(32).width(48)
621      Text("加载中")
622    }.width("100%")
623    .height(64)
624    .justifyContent(FlexAlign.Center)
625    // 当不处于加载中状态时隐藏组件。
626    .visibility(this.isLoading ? Visibility.Visible : Visibility.Hidden)
627  }
628
629  build() {
630    Refresh({ refreshing: $$this.refreshing, builder: this.refreshBuilder() }) {
631      List() {
632        ForEach(this.arr, (item: number) => {
633          ListItem() {
634            Text('' + item)
635              .width('100%')
636              .height(80)
637              .fontSize(16)
638              .textAlign(TextAlign.Center)
639              .backgroundColor(0xFFFFFF)
640          }.borderWidth(1)
641        }, (item: string) => item)
642
643        ListItem() {
644          this.footer();
645        }
646      }
647      .onScrollIndex((start: number, end: number) => {
648        // 当达到列表末尾时,触发新数据加载。
649        if (end >= this.arr.length - 1) {
650          this.isLoading = true;
651          // 模拟新数据加载。
652          setTimeout(() => {
653            for (let i = 0; i < 10; i++) {
654              this.arr.push(this.arr.length);
655            }
656            this.isLoading = false;
657          }, 700)
658        }
659      })
660      .scrollBar(BarState.Off)
661      // 开启边缘滑动效果。
662      .edgeEffect(EdgeEffect.Spring, { alwaysEnabled: true })
663    }
664    .width('100%')
665    .height('100%')
666    .backgroundColor(0xDCDCDC)
667    .onOffsetChange((offset: number) => {
668      this.refreshOffset = offset;
669    })
670    .onStateChange((state: RefreshStatus) => {
671      this.refreshState = state;
672    })
673    .onRefreshing(() => {
674      // 模拟数据刷新。
675      setTimeout(() => {
676        this.refreshing = false;
677      }, 2000)
678    })
679  }
680}
681```
682
683![refresh_boundary_resilience](figures/refresh_boundary_resilience.gif)
684
685### 示例7(设置最大下拉距离)
686
687通过[maxPullDownDistance](#maxpulldowndistance20)属性设置最大下拉距离。
688
689```ts
690// xxx.ets
691@Entry
692@Component
693struct RefreshExample {
694  @State isRefreshing: boolean = false
695  @State arr: string[] = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10']
696
697  build() {
698    Column() {
699      Refresh({ refreshing: $$this.isRefreshing }) {
700        List() {
701          ForEach(this.arr, (item: string) => {
702            ListItem() {
703              Text('' + item)
704                .width('70%')
705                .height(80)
706                .fontSize(16)
707                .margin(10)
708                .textAlign(TextAlign.Center)
709                .borderRadius(10)
710                .backgroundColor(0xFFFFFF)
711            }
712          }, (item: string) => item)
713        }
714        .onScrollIndex((first: number) => {
715          console.info(first.toString())
716        })
717        .width('100%')
718        .height('100%')
719        .alignListItem(ListItemAlign.Center)
720        .scrollBar(BarState.Off)
721      }
722      .maxPullDownDistance(150)
723      .onStateChange((refreshStatus: RefreshStatus) => {
724        console.info('Refresh onStatueChange state is ' + refreshStatus)
725      })
726      .onOffsetChange((value: number) => {
727        console.info('Refresh onOffsetChange offset:' + value)
728      })
729      .onRefreshing(() => {
730        setTimeout(() => {
731          this.isRefreshing = false
732        }, 2000)
733        console.log('onRefreshing test')
734      })
735      .backgroundColor(0x89CFF0)
736      .refreshOffset(64)
737      .pullToRefresh(true)
738    }
739  }
740}
741
742```
743
744![refresh_maxpulldowndistance](figures/refresh_maxpulldowndistance_demo_7.gif)
745
746### 示例8(禁止下拉刷新)
747
748通过[pullDownRatio](#pulldownratio12)属性禁止下拉刷新。
749
750```ts
751// xxx.ets
752@Entry
753@Component
754struct RefreshExample {
755  @State isRefreshing: boolean = false;
756  @State ratio: number | undefined = undefined;
757  @State arr: string[] = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10'];
758
759  build() {
760    Column() {
761      Row() {
762        Button('禁止下拉刷新').onClick(() => {
763          this.ratio = 0
764        })
765        Button('允许下拉刷新').onClick(() => {
766          this.ratio = undefined
767        })
768      }
769      Refresh({ refreshing: $$this.isRefreshing }) {
770          List() {
771            ForEach(this.arr, (item: string) => {
772              ListItem() {
773                Text('' + item)
774                  .width('70%')
775                  .height(80)
776                  .fontSize(16)
777                  .margin(10)
778                  .textAlign(TextAlign.Center)
779                  .borderRadius(10)
780                  .backgroundColor(0xFFFFFF)
781              }
782            }, (item: string) => item)
783          }
784          .onScrollIndex((first: number) => {
785            console.info(first.toString());
786          })
787          .width('100%')
788          .height('100%')
789          .alignListItem(ListItemAlign.Center)
790          .scrollBar(BarState.Off)
791      }
792      .backgroundColor(0x89CFF0)
793      .refreshOffset(64)
794      .pullToRefresh(true)
795      .pullDownRatio(this.ratio)
796      .onStateChange((refreshStatus: RefreshStatus) => {
797        console.info('Refresh onStatueChange state is ' + refreshStatus);
798      })
799      .onOffsetChange((value: number) => {
800        console.info('Refresh onOffsetChange offset:' + value);
801      })
802      .onRefreshing(() => {
803        setTimeout(() => {
804          this.isRefreshing = false;
805        }, 2000)
806        console.log('onRefreshing test');
807      })
808    }
809  }
810}
811```
812
813![refresh_pulldownratio](figures/refresh_pulldownratio.gif)
814
815### 示例9(不满一屏场景实现下拉刷新)
816
817通过设置[edgeEffect](ts-container-scrollable-common.md#edgeeffect11)属性中的alwaysEnabled参数,可以在不满一屏的情况下实现Refresh组件的下拉刷新效果。
818
819```ts
820// xxx.ets
821@Entry
822@Component
823struct RefreshExample {
824  @State isRefreshing: boolean = false;
825  @State alwaysEnabled: boolean = false;
826
827  build() {
828    Column() {
829      Refresh({ refreshing: $$this.isRefreshing }) {
830        Column() {
831          List() {
832            ListItem() {
833              Text('alwaysEnabled:' + this.alwaysEnabled)
834                .width('70%')
835                .height(80)
836                .fontSize(16)
837                .margin(10)
838                .textAlign(TextAlign.Center)
839                .borderRadius(10)
840                .backgroundColor(0xFFFFFF)
841                .onClick(() => {
842                  this.alwaysEnabled = !this.alwaysEnabled;
843                })
844            }
845          }
846          .width('100%')
847          .height('100%')
848          .alignListItem(ListItemAlign.Center)
849          .scrollBar(BarState.Auto)
850          // List组件内容大小小于组件自身且alwaysEnabled为false时,List不会响应手势,此时手势会被Column组件响应,不会产生下拉刷新效果
851          // alwaysEnabled设为true,List会响应手势并通过嵌套滚动带动Refresh组件产生下拉刷新效果
852          .edgeEffect(EdgeEffect.Spring, { alwaysEnabled: this.alwaysEnabled })
853        }
854        .gesture(
855          PanGesture({ direction: PanDirection.Vertical })
856        )
857      }
858      .onStateChange((refreshStatus: RefreshStatus) => {
859        console.info('Refresh onStatueChange state is ' + refreshStatus);
860      })
861      .onOffsetChange((value: number) => {
862        console.info('Refresh onOffsetChange offset:' + value);
863      })
864      .onRefreshing(() => {
865        setTimeout(() => {
866          this.isRefreshing = false;
867        }, 2000)
868      })
869      .backgroundColor(0x89CFF0)
870      .refreshOffset(64)
871      .pullToRefresh(true)
872    }
873  }
874}
875```
876
877![refresh_list_edgeEffect](figures/refresh_alwaysEnabled.gif)
878