美文网首页
二、HarmonyOS Next -- 列表数据的下拉刷新和上拉

二、HarmonyOS Next -- 列表数据的下拉刷新和上拉

作者: 人间四月天_Andy | 来源:发表于2025-02-20 09:04 被阅读0次

此文章仅供学习,不作其他用途。
如有侵权,请联系删除。
欢迎批评指正!!! 谢谢!!!
官网地址:https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/ts-container-refresh-V5#%E5%AD%90%E7%BB%84%E4%BB%B6

1、下拉刷新

Refresh组件:官方提供的下拉组件。会跟随手势下拉儿下移。我们只需要在下拉到某个位置时触发 相关的回调函数,并调用数据接口重新请求数据即可实现下拉数显功能。

1.1 Refresh组件的相关说明

1.1.1 接口:

Refresh(value: RefreshOptions)

1.1.2 RefreshOptions 对象

refreshing:

  • 必填
  • boolean类型
  • 组件是否处于刷新状态。true表示刷新状态,false表示未刷新状态。使用时需要使用 $$双向绑定该变量,否则会显示异常。

builder

  • 非必填
  • CustomBuilder类型
  • 自定义组件。自定义刷新区域显示内容。建议使用refreshingContent参数替代builder参数

refreshingContent:

  • 非必填
  • ComponentContent类型
  • 自定义组件。自定义刷新区域显示内容。优先级大于 builder参数。建议对自定义组件设置最小高度约束来避免自定义组件高度小于预期的情况发生。

promptText:

  • 非必填
  • ResourceStr类型
  • 设置刷新区域底部显示的自定义文本。优先级低于 builder 和 refreshingContentpromptText设置有效时,refreshOffset属性默认值为96vp
1.1.3 相关属性

I. refreshOffset

refreshOffset(value: number): value必填,设置触发刷新的下拉偏移量,当下拉距离小于该属性设置值时离手不会触发刷新。

  • value: 下拉偏移量,单位vp

II. pullToRefresh

pullToRefresh(value: boolean): value必填,设置当下拉距离超过refreshOffset时是否能触发刷新。true表示能触发刷新。false表示不能触发刷新。默认值true

III. pullDownRatio

pullDownRatio(ratio: Optional<number>): ratio必填。下拉跟手系数。数值越大,跟随手势下拉的反应越灵敏。

  • 0: 表示,不跟随手势下拉
  • 1: 表示等比例跟随手势下拉
  • 没有设置或设置为undefined时,默认使用动态下拉跟手系数,下拉距离越大,跟手系数越小。有效值为0-1之间的值,小于0的值会被视为0,大于1的值会被视为1
1.1.4 事件(回调函数)

I. onStateChange

onStateChange(callback: (state: RefreshStatus) => void): 当刷新状态变更时,触发回调。
RefreshStatus

  • Inactive: 0, 默认未下拉状态
  • Drag: 1, 下拉中,下拉距离小于刷新距离
  • OverDrag: 2, 下拉中,下拉距离超过刷新距离
  • Refresh: 3, 下拉结束,回弹至刷新距离,进入刷新中状态。
  • Done: 4, 刷新结束,返回初始状态(顶部)

II. onRefreshing

onRefreshing(callback: () => void): 进入刷新状态时触发回调。

III. onOffsetChange

onOffsetChange(callback: Callback<number>): 下拉距离发生变化时触发回调

1.2 Refresh组件的代码示例

Refresh({ refreshing: $$this.isRefreshing, promptText: this.refreshMessageText }) {
          List() {
            LazyForEach(this.dataSource, (secondItem: ListInfo) => {
              ListItem() {
                Row() {
                  Image(secondItem.indexNavPic)
                    .width('130vp')
                    .height('80vp')
                    .objectFit(ImageFit.TOP_START)
                    .borderRadius('8vp')
                    .margin({ right: '12vp' })

                  Column() {
                    Text(secondItem.activityName)
                      .width('190vp')
                      .textOverflow({ overflow: TextOverflow.Ellipsis })
                      .maxLines(1)
                      .fontSize('16fp')
                      .fontWeight(FontWeight.Medium)
                    Text(secondItem.theme)
                      .width('190vp')
                      .textOverflow({ overflow: TextOverflow.Ellipsis })
                      .maxLines(2)
                      .margin({ top: '4vp' })
                      .fontSize('12fp')
                      .fontColor('#99182431')
                    Row() {
                      Image(this.pictureUri)
                        .width('20vp')
                        .opacity(0.5)
                    }
                    .width('170vp')
                    .margin({ top: '10.5vp' })
                    .justifyContent(FlexAlign.End)
                    .alignItems(VerticalAlign.Bottom)
                  }
                  .alignItems(HorizontalAlign.Start)
                }
                .width('100%')
                .padding({
                  left: '12vp',
                  right: '12vp',
                  top: '12vp',
                  bottom: '12vp'
                })
                .justifyContent(FlexAlign.SpaceBetween)
              }
              .margin({ bottom: '8vp' })
              .borderRadius('16vp')
              .backgroundColor('#ffffff')
              .align(Alignment.TopStart)
              .width('100%')
            }, (secondItem: ListInfo) => JSON.stringify(secondItem))
            ListItem() {
              this.loadingFooterComponent();
            }
          }
          .scrollBar(BarState.Off)
          .width('100%')
          .onScrollIndex((start: number, end: number, center: number) => {
            // 如果列表滑动到最后一个索引值等于数据源的个数,说明已经滑动到了对底部,可以加载更多数据了。
            if (end === this.dataSource.totalCount() - 1) {
              this.isLoading = true;
              this.requestData(false);
            }
          })
        }
        .refreshOffset(this.refreshOffset)
        // 当达到下拉刷新的状态时(也就是下拉刷新的偏移量达到了设置的 refreshOffset 值),触发此回调函数
        .onRefreshing(() => {
          this.requestData(true);
        })

如下图所示:


image.png

1.3 自定义Refresh组件的下拉样式

1.3.1 CustomBuilder模式

builder
代码如下:

@Builder
  refreshHeaderBuilder() {
    Stack() {
      Row() {
        Image($r('app.media.refresh_load_image'))
          .height(32)
        Text('使劲往下拽').fontSize(16).margin({ left: 20 })
      }
      .alignItems(VerticalAlign.Center)
    }
    .align(Alignment.Center)
    .clip(true)
    // 设置最小高度约束保证自定义组件高度随刷新区域高度变化时自定义组件高度不会低于minHeight
    .constraintSize({ minHeight: 32 })
    .width("100%")
  }
Refresh({ refreshing: $$this.isRefreshing, promptText: this.refreshMessageText, builder: this.refreshHeaderBuilder }) {...}

当设置了 自定义的下拉刷新,则 promptText的设置就会失效。
如下图所示:


builder模式
1.3.2 ComponentContent模式

refreshingContent属性
代码如下:
I. 先自定义视图

@Builder
function customRefreshingContent() {
  Stack() {
    Column() {
      Image($r('app.media.refresh_icon'))
        .height(32)
      Text('使劲拽啊...')
        .fontSize(16)
        .margin({ top: 10 })
    }
    .alignItems(HorizontalAlign.Center)
  }
  .width('100%')
}

II. 自定义属性

// 自定义下拉刷新视图
  private contentHeader?: ComponentContent<object> = undefined;

III. 在aboutToAppear方法中添加如下代码

let uiContext = this.getUIContext();
    this.contentHeader = new ComponentContent(uiContext, wrapBuilder(customRefreshingContent))

IV. 在Refresh组件中添加refreshingConent属性, 代码如下

Refresh({
          refreshing: $$this.isRefreshing,
          promptText: this.refreshMessageText,
          builder: this.refreshHeaderBuilder,
          refreshingContent: this.contentHeader
        }) {...}

注意:这里设置了 promptTextbuilderrefreshingContent, 根据优先级顺序refreshingContent > builder > promptText, 所以只有refreshingContent属性生效。
如下图所示:

image.png

2. 上拉加载更多

上拉加载的功能实现比下拉刷新简单的多

2.1 自定义上拉加载组件

@Builder
  loadingFooterComponent() {
    Row() {
      Progress({ value: 10, type: ProgressType.Ring })
        .style({ status: ProgressStatus.LOADING })
        .color(Color.Black)
        .height(32)
        .visibility(this.isLoading ? Visibility.Visible : Visibility.Hidden)
      Text(this.loadingMessageText)
        .fontSize(16)
        .margin({ left: 10 })
    }
    .width('100%')
    .padding(10)
    .backgroundColor('#f1f3f5')
    .justifyContent(FlexAlign.Center)
    // .visibility(this.isLoading ? Visibility.Visible : Visibility.Hidden)
  }

2.2 设置 List组件中的最后一个ListItem 为 自定义的上拉加载组件

List() {
            LazyForEach(this.dataSource, (secondItem: ListInfo) => {
              ListItem() {
                  ...
              }
            }, (secondItem: ListInfo) => JSON.stringify(secondItem))
            ListItem() {
              this.loadingFooterComponent();
            }
          }

2.3 设置List组件的回调函数onScrollIndex

List()
.onScrollIndex((start: number, end: number, center: number) => {
            // 如果列表滑动到最后一个索引值等于数据源的个数,说明已经滑动到了对底部,可以加载更多数据了。
            if (end === this.dataSource.totalCount() - 1) {
              // 是否正在上拉加载更多数据
              this.isLoading = true;
              this.requestData(false);
            }
          })

2.4 其他具体实现细节,自己可以按需实现。

示例图如下:
加载数据中示例:


加载更多数据

暂无更多数据示例:


暂无更所数据
具体的实现细节,需要用户根据请求的数据实现。

相关文章

网友评论

      本文标题:二、HarmonyOS Next -- 列表数据的下拉刷新和上拉

      本文链接:https://www.haomeiwen.com/subject/mtyepjtx.html