前言

“下拉刷新最新、上拉加载更多”——这是信息流 app 的标准交互。看起来简单,但新手常卡在"怎么触发"“数据怎么接”。
这篇文章用一个新闻列表的完整案例,把下拉刷新 + 上拉加载做成闭环:下拉从第一页重新拉,上拉追加下一页。代码可直接套你的项目。
A hand-drawn doodle illustration on pure white pap

下拉刷新:Refresh 组件

HarmonyOS 用 Refresh 包裹列表,下拉就触发 onRefreshing

代码实现

@Local news: NewsItem[] = []
@Local refreshing: boolean = false

build() {
  Refresh({ refreshing: $$this.refreshing, builder: this.refreshTip() }) {
    List() {
      ForEach(this.news, (n) => {
        ListItem() { NewsRow({ item: n }) }
      }, (n) => n.id)
    }
  }
  .onRefreshing(async () => {
    await this.loadFirstPage()        // 拉第一页
    this.refreshing = false           // 关闭刷新动画
  })
}

A hand-drawn doodle illustration on pure white pap

讲解:

  • Refresh 第一个参数是刷新状态($$this.refreshing 是双向绑定),true 时显示加载圈。
  • .onRefreshing(...):用户下拉松手后触发,在这里拉最新数据。
  • 拉完务必把 refreshing 设回 false,否则圈一直转。

坑:onRefreshing 是异步的,记得 await 拉完再关 refreshing,不然动画提前结束。

上拉加载:onReachEnd

列表滚到底部时,用 onReachEnd 触发加载更多:

核心代码

List() {
  // ...
}
.onReachEnd(async () => {
  if (this.loading || this.noMore) return   // 防重复 + 到底判断
  this.loading = true
  const next = await this.loadNextPage()
  this.news = this.news.concat(next)        // 追加,不替换
  this.loading = false
  if (next.length === 0) this.noMore = true
})

要点:

  • onReachEnd 在列表触底时触发。
  • this.news.concat(next)追加新数据,不是覆盖,否则前面的丢了。
  • loading 防重复触发(触底可能连发多次);noMore 标记"没有更多了",避免无限请求。

加载更多的小菊花

底部加个"加载中"提示:

A hand-drawn doodle illustration on pure white pap

完整示例

ListItem() {
  if (this.loading) {
    Row() { LoadingProgress().width(24).height(24); Text('加载中...') }
      .justifyContent(FlexAlign.Center).width('100%').padding(12)
  }
}

LoadingProgress 是系统自带的小转圈,配合"加载中"文字,体验完整。

完整闭环逻辑

交互 触发 行为
下拉 onRefreshing 拉第 1 页,替换全部
上拉触底 onReachEnd 拉下一页,追加
到底 noMore=true 停止请求,显示"没有更多"

常见误区(小白必踩)

误区 说明
onRefreshingawait 就关 refreshing 拉取是异步的,没等完就关,加载圈提前消失。
上拉用"替换"不用"追加" this.news = next(替换)会把前面丢光,要用 concat 追加。
不防重复请求 onReachEnd 触底可能连发,用 loading 标志拦住;到底用 noMore 停止。

下面这段代码可以直接复制到 DevEco Studio 里运行。建议你边读边敲,改一改文末「动手改一改」里的参数,亲眼看看效果。

完整可运行示例:下拉刷新 + 上拉加载

给列表加上完整的「刷新 / 加载更多」闭环。

代码解析

// List 的刷新与加载更多
@Entry
@Component
struct FeedList {
  @State data: number[] = [1, 2, 3]
  @State loading: boolean = false

  loadMore() {
    if (this.loading) return
    this.loading = true
    setTimeout(() => {                       // 模拟请求
      const start = this.data.length + 1
      for (let i = start; i < start + 3; i++) { this.data.push(i) }
      this.loading = false
    }, 800)
  }

  build() {
    List() {
      ForEach(this.data, (n: number) => {
        ListItem() { Text('条目 ' + n).padding(16) }
      })
    }
    .onReachEnd(() => { this.loadMore() })   // 滚到底触发加载
  }
}

你会看到什么:列表滚到底部自动追加 3 条新数据(带 800ms 加载感)。配合 Refresh 组件还能做顶部下拉刷新,形成完整列表交互。

动手改一改

  • Refresh({ refreshing: $$this.loading }) 包裹 List 做下拉刷新。
  • loadMore 里返回空数组,显示「没有更多了」状态。
  • LazyForEach 替换 ForEach 支撑更大列表。

写在最后

下拉刷新 + 上拉加载的闭环,核心就三句话:Refresh 管下拉重拉第一页、onReachEnd 管上拉追加、用 loading/noMore 防重复和越界

把这套直接套进你的列表页,用户一拉一滚就能翻完所有内容——信息流 app 的"体感"就是这么来的。记得刷新用"替换"、加载用"追加",方向别搞反。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐