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 \| 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 \| string | 否 | 是 | 下拉摩擦系数,取值范围为0到100。<br/>默认值:62<br/>- 0表示下拉刷新容器不跟随手势下拉而下拉。<br/>- 100表示下拉刷新容器紧紧跟随手势下拉而下拉。<br/>- 数值越大,下拉刷新容器跟随手势下拉的反应越灵敏。<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 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 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 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 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 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 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 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 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 878