1# Refresh 2 3 可以进行页面下拉操作并显示刷新动效的容器组件。 4 5> **说明:** 6> 7> - 该组件从API Version 8开始支持。后续版本如有新增内容,则采用上角标单独标记该内容的起始版本。 8> 9> - 该组件从API Version 12开始支持与垂直滚动的Swiper和Web的联动。当Swiper设置loop属性为true时,Refresh无法和Swiper产生联动。 10 11## 子组件 12 13支持单个子组件。 14 15从API version 11开始,Refresh子组件会跟随手势下拉而下移。 16 17## 接口 18 19Refresh(value: RefreshOptions) 20 21**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。 22 23**系统能力:** SystemCapability.ArkUI.ArkUI.Full 24 25**参数:** 26 27| 参数名 | 类型 | 必填 | 说明 | 28| -------- | -------- | -------- | -------- | 29| value | [RefreshOptions](#refreshoptions对象说明)| 是 | 刷新组件参数。 | 30 31## RefreshOptions对象说明 32 33**系统能力:** SystemCapability.ArkUI.ArkUI.Full 34 35| 名称 | 类型 | 必填 | 说明 | 36| ---------- | ---------------------------------------- | ---- | ---------------------------------------- | 37| refreshing | boolean | 是 | 组件当前是否处于刷新中状态。true表示处于刷新中状态,false表示未处于刷新中状态。<br/>默认值:false<br/>该参数支持[$$](../../../quick-start/arkts-two-way-sync.md)双向绑定变量。 <br/>**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。| 38| offset<sup>(deprecated)</sup> | number \| string | 否 | 下拉起点距离组件顶部的距离。<br/>默认值:16,单位vp <br/>从API version 11开始废弃,无替代接口<br/>**说明:**<br/>offset取值范围[0vp,64vp]。大于64vp按照64vp处理。不支持百分比,不支持负数 。| 39| friction<sup>(deprecated)</sup> | number \| string | 否 | 下拉摩擦系数,取值范围为0到100。<br/>默认值:62<br/>- 0表示下拉刷新容器不跟随手势下拉而下拉。<br/>- 100表示下拉刷新容器紧紧跟随手势下拉而下拉。<br/>- 数值越大,下拉刷新容器跟随手势下拉的反应越灵敏。<br/>从API version 11开始废弃,从API version 12开始,可用[pullDownRatio](#pulldownratio12)属性替代。 | 40| builder<sup>10+</sup> | [CustomBuilder](ts-types.md#custombuilder8) | 否 | 自定义刷新区域显示内容。<br/>**说明:**<br/>API version 10及之前版本,自定义组件的高度限制在64vp之内。API version 11及以后版本没有此限制。 <br/>自定义组件设置了固定高度时,自定义组件会以固定高度显示在刷新区域下方;自定义组件未设置高度时,自定义组件高度会自适应刷新区域高度,会发生自定义组件高度跟随刷新区域变化至0的现象。建议对自定义组件设置最小高度约束来避免自定义组件高度小于预期的情况发生,具体可参照[示例2](#示例2)。 <br/>从API version 12开始,建议使用refreshingContent参数替代builder参数自定义刷新区域显示内容,以避免刷新过程中因自定义组件销毁重建造成的动画中断问题。<br/>**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。| 41| 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开始,该接口支持在原子化服务中使用。| 42| refreshingContent<sup>12+</sup> | [ComponentContent](../js-apis-arkui-ComponentContent.md) | 否 | 自定义刷新区域显示内容。<br/>**说明:**<br/>与builder参数同时设置时builder参数不生效。<br/>自定义组件设置了固定高度时,自定义组件会以固定高度显示在刷新区域下方;自定义组件未设置高度时,自定义组件高度会自适应刷新区域高度,会发生自定义组件高度跟随刷新区域变化至0的现象。建议对自定义组件设置最小高度约束来避免自定义组件高度小于预期的情况发生,具体可参照[示例5](#示例5)。 <br/>**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。| 43 44> **补充说明:** 45> - 当未设置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)属性时不会生效。 46> - 当设置了builder或refreshingContent时,是通过更新子组件相对于Refresh组件的位置实现的下拉位移效果,下拉位移过程中可以触发子组件的[onAreaChange](ts-universal-component-area-change-event.md#onareachange)事件,子组件设置[position](ts-universal-attributes-location.md#position)属性时会固定子组件相对于Refresh组件的位置导致子组件不会跟手进行下拉位移。 47 48## 属性 49 50支持[通用属性](ts-universal-attributes-size.md)外,还支持以下属性: 51 52### refreshOffset<sup>12+</sup> 53 54refreshOffset(value: number) 55 56设置触发刷新的下拉偏移量,当下拉距离小于该属性设置值时离手不会触发刷新。 57 58**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 59 60**系统能力:** SystemCapability.ArkUI.ArkUI.Full 61 62**参数:** 63 64| 参数名 | 类型 | 必填 | 说明 | 65| ------ | ------------------------------------------- | ---- | ---------------------------------------------------------- | 66| value | number | 是 | 下拉偏移量,单位vp。<br/>默认值:未设置[promptText](#refreshoptions对象说明)参数时为64vp,设置了[promptText](#refreshoptions对象说明)参数时为96vp。 <br/>如果取值为0或负数的时候此接口采用默认值。| 67 68### pullToRefresh<sup>12+</sup> 69 70pullToRefresh(value: boolean) 71 72设置当下拉距离超过[refreshOffset](#refreshoffset12)时是否能触发刷新。 73 74**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 75 76**系统能力:** SystemCapability.ArkUI.ArkUI.Full 77 78**参数:** 79 80| 参数名 | 类型 | 必填 | 说明 | 81| ------ | ------------------------------------------- | ---- | ---------------------------------------------------------- | 82| value | boolean | 是 | 当下拉距离超过[refreshOffset](#refreshoffset12)时是否能触发刷新。true表示能触发刷新,false表示不能触发刷新。<br/>默认值:true | 83 84### pullDownRatio<sup>12+</sup> 85 86pullDownRatio(ratio: [Optional](ts-universal-attributes-custom-property.md#optional12)\<number>) 87 88设置下拉跟手系数。 89 90**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 91 92**系统能力:** SystemCapability.ArkUI.ArkUI.Full 93 94**参数:** 95 96| 参数名 | 类型 | 必填 | 说明 | 97| ------ | ------------------------------------------- | ---- | ---------------------------------------------------------- | 98| ratio | [Optional](ts-universal-attributes-custom-property.md#optional12)\<number> | 是 | 下拉跟手系数。数值越大,跟随手势下拉的反应越灵敏。0表示不跟随手势下拉,1表示等比例跟随手势下拉。<br/>没有设置或设置为undefined时,默认使用动态下拉跟手系数,下拉距离越大,跟手系数越小。<br/>有效值为0-1之间的值,小于0的值会被视为0,大于1的值会被视为1。 99 100## 事件 101 102除支持[通用事件](ts-universal-events-click.md)外,还支持以下事件: 103 104### onStateChange 105 106onStateChange(callback: (state: RefreshStatus) => void) 107 108当前刷新状态变更时,触发回调。 109 110**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。 111 112**系统能力:** SystemCapability.ArkUI.ArkUI.Full 113 114**参数:** 115 116| 参数名 | 类型 | 必填 | 说明 | 117| ------ | --------------------------------------- | ---- | ---------- | 118| state | [RefreshStatus](#refreshstatus枚举说明) | 是 | 刷新状态。 | 119 120### onRefreshing 121 122onRefreshing(callback: () => void) 123 124进入刷新状态时触发回调。 125 126**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。 127 128**系统能力:** SystemCapability.ArkUI.ArkUI.Full 129 130### onOffsetChange<sup>12+</sup> 131 132onOffsetChange(callback: Callback\<number>) 133 134下拉距离发生变化时触发回调。 135 136**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 137 138**系统能力:** SystemCapability.ArkUI.ArkUI.Full 139 140**参数:** 141 142| 参数名 | 类型 | 必填 | 说明 | 143| ------ | --------------------------------------- | ---- | ---------- | 144| value | number | 是 | 下拉距离。<br/>单位:vp | 145 146 147## RefreshStatus枚举说明 148 149**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。 150 151**系统能力:** SystemCapability.ArkUI.ArkUI.Full 152 153| 名称 | 值 | 说明 | 154| -------- | -------- | -------------------- | 155| Inactive | 0 | 默认未下拉状态。 | 156| Drag | 1 | 下拉中,下拉距离小于刷新距离。 | 157| OverDrag | 2 | 下拉中,下拉距离超过刷新距离。 | 158| Refresh | 3 | 下拉结束,回弹至刷新距离,进入刷新中状态。 | 159| Done | 4 | 刷新结束,返回初始状态(顶部)。 | 160 161 162## 示例 163 164### 示例1 165 166刷新区域使用默认样式。 167 168```ts 169// xxx.ets 170@Entry 171@Component 172struct RefreshExample { 173 @State isRefreshing: boolean = false 174 @State arr: String[] = ['0', '1', '2', '3', '4','5','6','7','8','9','10'] 175 176 build() { 177 Column() { 178 Refresh({ refreshing: $$this.isRefreshing}) { 179 List() { 180 ForEach(this.arr, (item: string) => { 181 ListItem() { 182 Text('' + item) 183 .width('70%').height(80).fontSize(16).margin(10) 184 .textAlign(TextAlign.Center).borderRadius(10).backgroundColor(0xFFFFFF) 185 } 186 }, (item: string) => item) 187 } 188 .onScrollIndex((first: number) => { 189 console.info(first.toString()) 190 }) 191 .width('100%') 192 .height('100%') 193 .alignListItem(ListItemAlign.Center) 194 .scrollBar(BarState.Off) 195 } 196 .onStateChange((refreshStatus: RefreshStatus) => { 197 console.info('Refresh onStatueChange state is ' + refreshStatus) 198 }) 199 .onOffsetChange((value: number) => { 200 console.info('Refresh onOffsetChange offset:' + value) 201 }) 202 .onRefreshing(() => { 203 setTimeout(() => { 204 this.isRefreshing = false 205 }, 2000) 206 console.log('onRefreshing test') 207 }) 208 .backgroundColor(0x89CFF0) 209 .refreshOffset(64) 210 .pullToRefresh(true) 211 } 212 } 213} 214``` 215 216 217 218### 示例2 219 220通过builder参数自定义刷新区域显示内容。 221 222```ts 223// xxx.ets 224@Entry 225@Component 226struct RefreshExample { 227 @State isRefreshing: boolean = false 228 @State arr: String[] = ['0', '1', '2', '3', '4','5','6','7','8','9','10'] 229 @Builder 230 customRefreshComponent() 231 { 232 Stack() 233 { 234 Row() 235 { 236 LoadingProgress().height(32) 237 Text("Refreshing...").fontSize(16).margin({left:20}) 238 } 239 .alignItems(VerticalAlign.Center) 240 } 241 .align(Alignment.Center) 242 .clip(true) 243 .constraintSize({minHeight:32}) // 设置最小高度约束保证自定义组件高度随刷新区域高度变化时自定义组件高度不会低于minHeight 244 .width("100%") 245 } 246 247 build() { 248 Column() { 249 Refresh({ refreshing: $$this.isRefreshing,builder:this.customRefreshComponent()}) { 250 List() { 251 ForEach(this.arr, (item: string) => { 252 ListItem() { 253 Text('' + item) 254 .width('70%').height(80).fontSize(16).margin(10) 255 .textAlign(TextAlign.Center).borderRadius(10).backgroundColor(0xFFFFFF) 256 } 257 }, (item: string) => item) 258 } 259 .onScrollIndex((first: number) => { 260 console.info(first.toString()) 261 }) 262 .width('100%') 263 .height('100%') 264 .alignListItem(ListItemAlign.Center) 265 .scrollBar(BarState.Off) 266 } 267 .backgroundColor(0x89CFF0) 268 .pullToRefresh(true) 269 .refreshOffset(64) 270 .onStateChange((refreshStatus: RefreshStatus) => { 271 console.info('Refresh onStatueChange state is ' + refreshStatus) 272 }) 273 .onRefreshing(() => { 274 setTimeout(() => { 275 this.isRefreshing = false 276 }, 2000) 277 console.log('onRefreshing test') 278 }) 279 } 280 } 281} 282``` 283 284 285 286### 示例3 287 288边界刷新回弹效果。 289 290```ts 291// xxx.ets 292@Entry 293@Component 294struct ListRefreshLoad { 295 @State arr: Array<number> = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; 296 @State refreshing: boolean = false; 297 @State refreshOffset: number = 0; 298 @State refreshState: RefreshStatus = RefreshStatus.Inactive; 299 @State canLoad: boolean = false; 300 @State isLoading: boolean = false; 301 302 @Builder 303 refreshBuilder() { 304 Stack({ alignContent: Alignment.Bottom }) { 305 // 可以通过刷新状态控制是否存在Progress组件 306 // 当刷新状态处于下拉中或刷新中状态时Progress组件才存在 307 if (this.refreshState != RefreshStatus.Inactive && this.refreshState != RefreshStatus.Done) { 308 Progress({ value: this.refreshOffset, total: 64, type: ProgressType.Ring }) 309 .width(32).height(32) 310 .style({ status: this.refreshing ? ProgressStatus.LOADING : ProgressStatus.PROGRESSING }) 311 .margin(10) 312 } 313 } 314 .clip(true) 315 .height("100%") 316 .width("100%") 317 } 318 319 @Builder 320 footer() { 321 Row() { 322 LoadingProgress().height(32).width(48) 323 Text("加载中") 324 }.width("100%") 325 .height(64) 326 .justifyContent(FlexAlign.Center) 327 // 当不处于加载中状态时隐藏组件 328 .visibility(this.isLoading ? Visibility.Visible : Visibility.Hidden) 329 } 330 331 build() { 332 Refresh({ refreshing: $$this.refreshing, builder: this.refreshBuilder() }) { 333 List() { 334 ForEach(this.arr, (item: number) => { 335 ListItem() { 336 Text('' + item) 337 .width('100%') 338 .height(80) 339 .fontSize(16) 340 .textAlign(TextAlign.Center) 341 .backgroundColor(0xFFFFFF) 342 }.borderWidth(1) 343 }, (item: string) => item) 344 345 ListItem() { 346 this.footer(); 347 } 348 } 349 .onScrollIndex((start: number, end: number) => { 350 // 当达到列表末尾时,触发新数据加载 351 if (this.canLoad && end >= this.arr.length - 1) { 352 this.canLoad = false; 353 this.isLoading = true; 354 // 模拟新数据加载 355 setTimeout(() => { 356 for (let i = 0; i < 10; i++) { 357 this.arr.push(this.arr.length); 358 this.isLoading = false; 359 } 360 }, 700) 361 } 362 }) 363 .onScrollFrameBegin((offset: number, state: ScrollState) => { 364 // 只有当向上滑动时触发新数据加载 365 if (offset > 5 && !this.isLoading) { 366 this.canLoad = true; 367 } 368 return { offsetRemain: offset }; 369 }) 370 .scrollBar(BarState.Off) 371 // 开启边缘滑动效果 372 .edgeEffect(EdgeEffect.Spring, { alwaysEnabled: true }) 373 } 374 .width('100%') 375 .height('100%') 376 .backgroundColor(0xDCDCDC) 377 .onOffsetChange((offset: number) => { 378 this.refreshOffset = offset; 379 }) 380 .onStateChange((state: RefreshStatus) => { 381 this.refreshState = state; 382 }) 383 .onRefreshing(() => { 384 // 模拟数据刷新 385 setTimeout(() => { 386 this.refreshing = false; 387 }, 2000) 388 }) 389 } 390} 391``` 392 393 394 395### 示例4 396 397通过promptText参数设置刷新区域显示文本。 398 399```ts 400// xxx.ets 401@Entry 402@Component 403struct RefreshExample { 404 @State isRefreshing: boolean = false 405 @State promptText: string = "Refreshing..." 406 @State arr: String[] = ['0', '1', '2', '3', '4','5','6','7','8','9','10'] 407 408 build() { 409 Column() { 410 Refresh({ refreshing: $$this.isRefreshing, promptText: this.promptText}) { 411 List() { 412 ForEach(this.arr, (item: string) => { 413 ListItem() { 414 Text('' + item) 415 .width('70%').height(80).fontSize(16).margin(10) 416 .textAlign(TextAlign.Center).borderRadius(10).backgroundColor(0xFFFFFF) 417 } 418 }, (item: string) => item) 419 } 420 .onScrollIndex((first: number) => { 421 console.info(first.toString()) 422 }) 423 .width('100%') 424 .height('100%') 425 .alignListItem(ListItemAlign.Center) 426 .scrollBar(BarState.Off) 427 } 428 .backgroundColor(0x89CFF0) 429 .pullToRefresh(true) 430 .refreshOffset(96) 431 .onStateChange((refreshStatus: RefreshStatus) => { 432 console.info('Refresh onStatueChange state is ' + refreshStatus) 433 }) 434 .onOffsetChange((value: number) => { 435 console.info('Refresh onOffsetChange offset:' + value) 436 }) 437 .onRefreshing(() => { 438 setTimeout(() => { 439 this.isRefreshing = false 440 }, 2000) 441 console.log('onRefreshing test') 442 }) 443 } 444 } 445} 446``` 447 448 449 450### 示例5 451 452通过refreshingContent参数自定义刷新区域显示内容 453 454```ts 455import { ComponentContent } from '@ohos.arkui.node'; 456 457class Params { 458 refreshStatus: RefreshStatus = RefreshStatus.Inactive 459 460 constructor(refreshStatus: RefreshStatus) { 461 this.refreshStatus = refreshStatus; 462 } 463} 464 465@Builder 466function customRefreshingContent(params:Params) { 467 Stack() { 468 Row() { 469 LoadingProgress().height(32) 470 Text("refreshStatus: "+params.refreshStatus).fontSize(16).margin({left:20}) 471 } 472 .alignItems(VerticalAlign.Center) 473 } 474 .align(Alignment.Center) 475 .clip(true) 476 .constraintSize({minHeight:32}) // 设置最小高度约束保证自定义组件高度随刷新区域高度变化时自定义组件高度不会低于minHeight 477 .width("100%") 478} 479 480@Entry 481@Component 482struct RefreshExample { 483 @State isRefreshing: boolean = false 484 @State arr: String[] = ['0', '1', '2', '3', '4','5','6','7','8','9','10'] 485 @State refreshStatus: RefreshStatus = RefreshStatus.Inactive 486 private contentNode?: ComponentContent<Object> = undefined 487 private params: Params = new Params(RefreshStatus.Inactive) 488 489 aboutToAppear():void { 490 let uiContext = this.getUIContext() 491 this.contentNode = new ComponentContent(uiContext, wrapBuilder(customRefreshingContent), this.params) 492 } 493 494 build() { 495 Column() { 496 Refresh({ refreshing: $$this.isRefreshing, refreshingContent:this.contentNode}) { 497 List() { 498 ForEach(this.arr, (item: string) => { 499 ListItem() { 500 Text('' + item) 501 .width('70%').height(80).fontSize(16).margin(10) 502 .textAlign(TextAlign.Center).borderRadius(10).backgroundColor(0xFFFFFF) 503 } 504 }, (item: string) => item) 505 } 506 .onScrollIndex((first: number) => { 507 console.info(first.toString()) 508 }) 509 .width('100%') 510 .height('100%') 511 .alignListItem(ListItemAlign.Center) 512 .scrollBar(BarState.Off) 513 } 514 .backgroundColor(0x89CFF0) 515 .pullToRefresh(true) 516 .refreshOffset(96) 517 .onStateChange((refreshStatus: RefreshStatus) => { 518 this.refreshStatus = refreshStatus 519 this.params.refreshStatus = refreshStatus 520 this.contentNode?.update(this.params) // 更新自定义组件内容 521 console.info('Refresh onStatueChange state is ' + refreshStatus) 522 }) 523 .onRefreshing(() => { 524 setTimeout(() => { 525 this.isRefreshing = false 526 }, 2000) 527 console.log('onRefreshing test') 528 }) 529 } 530 } 531} 532``` 533 534 535### 示例6 536 537通过[pullDownRatio](#pulldownratio12)属性和[onOffsetChange](#onoffsetchange12)事件实现最大下拉距离 538 539```ts 540import { ComponentContent } from '@ohos.arkui.node'; 541 542@Builder 543function customRefreshingContent() { 544 Stack() { 545 Row() { 546 LoadingProgress().height(32) 547 } 548 .alignItems(VerticalAlign.Center) 549 } 550 .align(Alignment.Center) 551 .clip(true) 552 .constraintSize({minHeight:32}) // 设置最小高度约束保证自定义组件高度随刷新区域高度变化时自定义组件高度不会低于minHeight 553 .width("100%") 554} 555 556@Entry 557@Component 558struct RefreshExample { 559 @State isRefreshing: boolean = false 560 @State arr: String[] = ['0', '1', '2', '3', '4','5','6','7','8','9','10'] 561 @State maxRefreshingHeight: number = 100.0 562 @State ratio: number = 1 563 private contentNode?: ComponentContent<Object> = undefined 564 565 aboutToAppear():void { 566 let uiContext = this.getUIContext(); 567 this.contentNode = new ComponentContent(uiContext, wrapBuilder(customRefreshingContent)) 568 } 569 570 build() { 571 Column() { 572 Refresh({ refreshing: $$this.isRefreshing, refreshingContent:this.contentNode}) { 573 List() { 574 ForEach(this.arr, (item: string) => { 575 ListItem() { 576 Text('' + item) 577 .width('70%').height(80).fontSize(16).margin(10) 578 .textAlign(TextAlign.Center).borderRadius(10).backgroundColor(0xFFFFFF) 579 } 580 }, (item: string) => item) 581 } 582 .onScrollIndex((first: number) => { 583 console.info(first.toString()) 584 }) 585 .width('100%') 586 .height('100%') 587 .alignListItem(ListItemAlign.Center) 588 .scrollBar(BarState.Off) 589 } 590 .backgroundColor(0x89CFF0) 591 .pullDownRatio(this.ratio) 592 .pullToRefresh(true) 593 .refreshOffset(64) 594 .onOffsetChange((offset: number)=>{ 595 this.ratio = 1 - Math.pow((offset / this.maxRefreshingHeight), 3) // 越接近最大距离,下拉跟手系数越小 596 }) 597 .onStateChange((refreshStatus: RefreshStatus) => { 598 console.info('Refresh onStatueChange state is ' + refreshStatus) 599 }) 600 .onRefreshing(() => { 601 setTimeout(() => { 602 this.isRefreshing = false 603 }, 2000) 604 console.log('onRefreshing test') 605 }) 606 } 607 } 608} 609``` 610 611