鸿蒙 LazyForEach实现按需加载

本文同步发表于我的微信公众号,微信搜索 程语新视界 即可关注,每个工作日都有文章更新

鸿蒙(HarmonyOS)开发中,IDataSource 接口是数据源管理的核心接口,主要用于为列表类组件(如ListGridWaterFlow等)提供数据绑定和动态更新能力。

一、IDataSource 接口的作用

  1. 数据抽象与统一访问
    • 将数据源与UI组件解耦,提供标准化的数据访问方法(如获取数据长度、按索引读取数据等)。
  2. 动态数据更新
    • 支持数据变化时自动通知UI刷新(通过监听器机制)。
  3. 性能优化
    • 结合LazyForEach实现按需加载,减少内存占用 。

二、核心方法与说明

方法作用
getData(index: number)返回指定索引的数据项
getTotalCount(): number返回数据总长度
registerDataChangeListener()注册数据变化监听器,数据更新时触发UI刷新
unregisterDataChangeListener()取消监听器注册

三、使用步骤示例

1. 实现自定义数据源类
// 自定义数据源实现
export class MyDataSource implements IDataSource {
  private dataArray: string[] = ['Item 1', 'Item 2', 'Item 3'];
  private listeners: DataChangeListener[] = [];

  // 获取数据长度
  getTotalCount(): number {
    return this.dataArray.length;
  }

  // 按索引获取数据
  getData(index: number): string {
    return this.dataArray[index];
  }

  // 注册数据变化监听器
  registerDataChangeListener(listener: DataChangeListener): void {
    if (this.listeners.indexOf(listener) < 0) {
      this.listeners.push(listener);
    }
  }

  // 取消注册监听器
  unregisterDataChangeListener(listener: DataChangeListener): void {
    const pos = this.listeners.indexOf(listener);
    if (pos >= 0) {
      this.listeners.splice(pos, 1);
    }
  }

  // 通知数据变化(手动触发刷新)
  notifyDataReload(): void {
    this.listeners.forEach(listener => listener.onDataReloaded());
  }

  // 添加新数据
  addData(item: string): void {
    this.dataArray.push(item);
    this.notifyDataReload();
  }
}

 2. 在UI组件中绑定数据源

@Entry
@Component
struct DataSourceExample {
  private dataSource: MyDataSource = new MyDataSource();

  build() {
    List({ space: 10 }) {
      LazyForEach(this.dataSource, (item: string) => {
        ListItem() {
          Text(item).fontSize(16)
        }
      }, (item: string) => item) // 唯一键保证复用
    }
    .onClick(() => {
      this.dataSource.addData(`New Item ${Date.now()}`); // 动态添加数据
    })
  }
}

四、动态数据更新场景

1. 数据变化自动刷新UI

当调用addData()方法时,notifyDataReload()会触发所有已注册的监听器,通知List重新渲染。

2. 与后台API结合
// 模拟从网络加载数据
fetchDataFromApi() {
  const newData = await httpRequest('https://api.example.com/items');
  this.dataArray = [...this.dataArray, ...newData];
  this.notifyDataReload();
}

五、总结

  1. 性能优化
    • 对于大数据集,优先使用LazyForEach而非ForEach,避免全量渲染 。
  2. 内存管理
    • 在页面销毁时取消所有监听器注册(aboutToDisappear生命周期中调用unregisterDataChangeListener)。
  3. 复杂数据处理
    • 在数据源内部实现过滤、排序逻辑,减少UI层计算负担。

六、对比其他数据管理方式

特性IDataSource直接数组绑定@State + 数组
动态更新✅ 自动通知❌ 需手动触发✅ 自动响应
性能✅ 按需渲染❌ 全量渲染❌ 全量渲染
适用场景长列表、动态数据静态小数据集简单状态管理

 通过IDataSource接口,开发者可以高效管理动态数据源,尤其适合需要频繁更新的列表场景。

### HarmonyOSLazyForEach 的使用方法 #### 什么是 LazyForEach? `LazyForEach` 是 HarmonyOS ArkUI 提供的一种优化机制,用于处理长列表渲染场景。它通过按需加载的方式,在滚动容器中仅创建当前可见区域内的组件实例,从而显著减少内存消耗并提升性能[^1]。 --- #### LazyForEach 的核心特性 1. **按需创建组件** 当 `LazyForEach` 被放置在一个可滚动的容器(如 `List` 或 `ScrollView`)中时,框架只会为可视范围内的数据项生成组件实例。对于未进入视口的数据项,则不会立即创建对应的 UI 组件。 2. **组件复用与缓存** 配合 `cachedCount` 参数可以进一步提高性能。`cachedCount` 定义了一个缓冲区大小,用来保存已滑出屏幕但仍可能再次进入视口的组件实例,避免频繁销毁和重建操作[^2]。 3. **键值生成规则** 每个数据项都需要有一个唯一的键值来标识自己。可以通过自定义函数指定键值生成逻辑。例如,在某些情况下可以直接利用数组索引作为键值;而在其他更复杂的情况下则推荐基于业务字段构建唯一 ID[^3]。 --- #### 基本语法结构 以下是标准的 `LazyForEach` 使用方式: ```xml <List> <LazyForEach items="{dataList}" cachedCount="5"> <template slot="item" data="{item}"> <!-- 单个列表项的内容 --> <ListItem key="{item.id}"> {item.name} </ListItem> </template> </LazyForEach> </List> ``` - **items**: 数据源绑定属性,通常是一个 JSON 数组对象 `{id, name}` 等形式表示每条记录的信息; - **cachedCount**: 缓冲池容量设置,默认值一般设为较小数值即可满足大多数需求; - **slot="item"**: 插槽名称固定写成 `"item"` ,代表单个项目模板布局描述部分; - **key**: 设置每一行独立识别标志符 (类似于 Vue.js 中 v-for 所需 :key 属性),确保虚拟 DOM 更新效率最大化。 --- #### 示例代码展示 下面给出一段完整的例子演示如何运用以上知识点实现动态加载效果良好的垂直方向线性排列型界面设计模式下的商品详情页浏览功能模块开发流程说明文档编写技巧分享心得总结如下所示: 假设我们拥有一份包含多个产品的JSON文件productData.json: ```json [ {"id": "p001", "name":"Product One"}, {"id": "p002", "name":"Product Two"}, ... ] ``` 那么我们的页面XML应该这样书写: ```xml <DirectionalLayout xmlns:ohos="http://schemas.huawei.com/res/ohos" ohos:height="match_parent" ohos:width="match_parent" ohos:orientation="vertical"> <List ohos:id="$+id:list_view" ohos:height="match_content" ohos:width="match_parent"/> </DirectionalLayout> ``` 接着就是Java/Kotlin端的部分啦! 这里以Kotlin为例: ```kotlin @Entry @Component class ProductListView { private val productList = Json.decodeFromString<ArrayList<Product>>(context.getResourceManager().getElement(ResourceTable.Rawfile_product_data).getString()) @Component.Build fun build(): Component { return DirectionalLayout(context).apply { height = Dimension.MATCH_PARENT width = Dimension.MATCH_PARENT orientation = LayoutOrientation.VERTICAL List(componentContext).also { list -> list.height = Dimension.MATCH_CONTENT list.width = Dimension.MATCH_PARENT list.listProvider = object : List.ListProvider { override fun getCount(): Int = productList.size override fun getComponent(position: Int): Component? = ListItemBuilder.build(productList[position]) } add(list) } } } } object ListItemBuilder{ fun build(item: Product): Component { return Text(context).apply { text = item.name textSize = 16f textColor = Color.BLACK height = Dimension.WRAP_CONTENT width = Dimension.MATCH_PARENT }.let(::ListItem) } } ``` 注意这里为了简化起见省去了很多实际项目中的必要步骤比如异常捕获、资源管理等等细节内容哦~ --- ### 注意事项 虽然 `LazyForEach` 可以为应用带来诸多好处,但在具体实施过程中仍需要注意以下几个方面: - 不要在内部嵌套额外复杂的控制流语句(如果...否则...)以免破坏原有预期行为; - 正确配置好 `cachedCount` 大小以便找到最佳平衡点兼顾流畅度与资源利用率之间的关系. ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值