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