鸿蒙应用开发实战:轻松实现列表上拉加载更多

这是一个使用鸿蒙技术开发的本地原生记账应用,非常适合大家用来练手。相关源码已上传至 Github,点击此处查看项目。欢迎大家交流、指正,也欢迎提交 PR。

一、引言

在移动应用开发中,列表的"上拉加载更多"是一个极其常见的交互模式。当用户滑动列表到底部时,自动加载下一页数据,能够极大地提升用户体验。在鸿蒙(HarmonyOS)应用开发中,实现这一效果同样非常便捷。

本文将带你从零开始,手把手实现一个带有"正在加载中"提示的上拉加载效果,让你快速掌握其核心实现原理。

二、效果预览与核心思路

我们先来看一下最终要实现的效果:当列表滑动到底部时,底部会显示一个"正在加载中..."的提示,模拟网络请求完成后,新的数据被追加到列表中,加载提示消失。

实现这个效果,主要依赖以下几个核心点:

  1. List 组件:作为列表的容器,提供滚动和布局能力。
  2. onReachEnd 事件 :List 组件提供的一个回调事件,当列表滚动到底部时自动触发。
  3. 状态管理 :通过一个 @State 变量来控制"加载中"状态的显示与隐藏。
  4. 数据模拟 :使用 setTimeout 模拟异步网络请求。

三、代码实现与详解

下面我们直接进入代码环节,看看如何一步步构建这个功能。

3.1 定义数据与状态

首先,我们需要定义列表的数据源和一个用于标识加载状态的状态变量。

typescript 复制代码
@Entry
@Component
struct PullUpLoadDemo {
  // 列表数据源
  @State private listData: string[] = [];
  // 是否正在加载中
  @State private isLoading: boolean = false;

  aboutToAppear() {
    // 初始化时加载第一页数据
    this.loadMoreData();
  }

  // 模拟加载更多数据
  loadMoreData() {
    // 模拟网络请求延迟
    setTimeout(() => {
      // 生成新的数据项
      let newData: string[] = [];
      for (let i = 0; i < 10; i++) {
        newData.push(`列表项 ${this.listData.length + i + 1}`);
      }
      // 将新数据追加到现有数据中
      this.listData = [...this.listData, ...newData];
      // 加载完成,更新状态
      this.isLoading = false;
    }, 1500); // 模拟1.5秒的网络延迟
  }

  build() {
    // ... 构建UI
  }
}

3.2 构建列表 UI

接下来,在 build 方法中构建我们的列表 UI。关键在于 List 组件的 onReachEnd 事件。

typescript 复制代码
  build() {
    Column() {
      List() {
        // 1. 使用 ForEach 渲染数据列表
        ForEach(this.listData, (item: string) => {
          ListItem() {
            Text(item)
              .width('100%')
              .height(80)
              .fontSize(18)
              .textAlign(TextAlign.Center)
              .backgroundColor(Color.White)
              .borderRadius(10)
              .margin({ top: 10 })
          }
        }, (item: string) => item)

        // 2. 在列表末尾添加"加载中"提示项
        if (this.isLoading) {
          ListItem() {
            Row() {
              LoadingProgress()
                .width(24)
                .height(24)
                .margin({ right: 10 })
              Text('正在加载中...')
                .fontSize(16)
                .fontColor('#666')
            }
            .justifyContent(FlexAlign.Center)
            .width('100%')
            .height(60)
          }
        }
      }
      .width('100%')
      .height('100%')
      .backgroundColor('#F5F5F5')
      // 3. 核心:监听列表滚动到底部事件
      .onReachEnd(() => {
        // 如果当前正在加载,则直接返回,避免重复请求
        if (this.isLoading) {
          return;
        }
        // 设置加载状态为 true,显示加载提示
        this.isLoading = true;
        // 调用加载数据的方法
        this.loadMoreData();
      })
    }
    .width('100%')
    .height('100%')
    .padding(10)
  }

四、完整代码示例

为了方便你直接运行和测试,这里提供完整的代码片段。

typescript 复制代码
@Entry
@Component
struct PullUpLoadDemo {
  @State private listData: string[] = [];
  @State private isLoading: boolean = false;

  aboutToAppear() {
    this.loadMoreData();
  }

  loadMoreData() {
    setTimeout(() => {
      let newData: string[] = [];
      for (let i = 0; i < 10; i++) {
        newData.push(`列表项 ${this.listData.length + i + 1}`);
      }
      this.listData = [...this.listData, ...newData];
      this.isLoading = false;
    }, 1500);
  }

  build() {
    Column() {
      List() {
        ForEach(this.listData, (item: string) => {
          ListItem() {
            Text(item)
              .width('100%')
              .height(80)
              .fontSize(18)
              .textAlign(TextAlign.Center)
              .backgroundColor(Color.White)
              .borderRadius(10)
              .margin({ top: 10 })
          }
        }, (item: string) => item)

        if (this.isLoading) {
          ListItem() {
            Row() {
              LoadingProgress()
                .width(24)
                .height(24)
                .margin({ right: 10 })
              Text('正在加载中...')
                .fontSize(16)
                .fontColor('#666')
            }
            .justifyContent(FlexAlign.Center)
            .width('100%')
            .height(60)
          }
        }
      }
      .width('100%')
      .height('100%')
      .backgroundColor('#F5F5F5')
      .onReachEnd(() => {
        if (this.isLoading) {
          return;
        }
        this.isLoading = true;
        this.loadMoreData();
      })
    }
    .width('100%')
    .height('100%')
    .padding(10)
  }
}

运行效果:

五、总结

通过以上步骤,我们成功地在鸿蒙应用中实现了列表的上拉加载更多功能。核心逻辑可以总结为:

  1. 监听事件 :利用 List 组件的 onReachEnd 事件监听滚动到底部。
  2. 状态控制 :通过 isLoading 状态变量防止重复加载,并控制加载提示的显示。
  3. 数据追加:在模拟的异步请求完成后,将新数据追加到原有数据源中。

这个模式非常通用,你可以轻松地将它应用到实际项目中的分页数据加载场景。希望这篇教程对你有所帮助!

相关推荐
miss14 分钟前
AI 改完不敢上线?OpenSpec 规约驱动开发实战
前端·代码规范·前端工程化
AiMan1 小时前
腾讯ima还是不可以导出左侧所有对话,只能单条导出,方法不对吗
前端
去伪存真1 小时前
并发量一高就装死?Agent 全链路“防猝死”手册
前端·agent
Moment1 小时前
AI 编程时代,聊聊我的 Mac mini 双屏桌面
前端·后端
AiMan1 小时前
先确认能不能一次把Microsofat Copilot所有内容打包一个文件导出
前端
总要学点什么1 小时前
第97篇:三维数据加载-城市建筑白模自定义着色器——让一条光带沿高度扫上去,快慢却由帧率说了算
前端·gis·webgl·cesium·着色器·空间分析·白模
上海懒图科技2 小时前
工厂厂区电子地图定位导航:厂区人车精准定位,安全合规双保障
前端·arcgis·vue
西安小哥2 小时前
端侧跑大模型,真正的门槛不在参数量:一次 596 秒到 10 秒的压缩路径复盘
前端·人工智能
陈随易2 小时前
Cloudflare 出钱,Vite 团队挑人,44 个维护者分 30 万美元
前端·后端·程序员
恋猫de小郭3 小时前
Flutter THLC 的 WebView 滚动终于从 70 FPS 提升到 120 FPS
android·前端·flutter