【HarmonyOS NEXT】解决Repeat复用导致Image加载图片展示的是上一张图片的问题

1. 背景

在使用Repeat+virtualScroll+image 的时候,滑动列表到第二屏的时候会发现Image 的alt 失效,先展示的是上一屏的图片,然后才会展示最新的图片,效果如下

代码如下

@Entry
@ComponentV2
struct Index {
  @Local dataSource: Array<string> = []

  aboutToAppear(): void {
    for (let i = 0; i < 1000; i++) {
      this.dataSource.push(`https://placehold.co/400x300/orange/white/png?text=testimage_${i}`)
    }
  }

  build() {
    Column() {
      WaterFlow() {
        Repeat(this.dataSource)
          .virtualScroll()
          .each(() => {
          })
          .key((item) => {
            return item
          })
          .templateId((item) => {
            return 'templateImage'
          })
          .template("templateImage", (ri) => {
            Image(ri.item)
              .width('100%')
              .alt($r('app.media.ic_placeholder_400x300'))
              .aspectRatio(4 / 3)
          }, { cachedCount: 10 })
      }
      .width('100%')
      .height('100%')
      .cachedCount(0)
      .columnsTemplate('1fr 1fr')
      .rowsTemplate('1fr 1fr')
      .columnsGap(3)
      .rowsGap(3)
    }
  }
}

1.1 问题原因

和行业内的大佬沟通,发现这个问题其实在2024年11月左右就已经存在了,原因是image的问题,没有适配好repeat,导致alt 在Repeat virtualScroll image 复用的情况下无效,加载新的图片时,展示的还是上一次src的图片。

1.2 解决办法

既然是复用,那么可以在封装一个自定义image组件,内部监听src的变化,如果变化,则重新生成一个新的image 让image重复上下树

下面是解决后的效果

下面是解决代码

@Entry
@ComponentV2
struct Index {
  @Local dataSource: Array<string> = []

  aboutToAppear(): void {
    for (let i = 0; i < 1000; i++) {
      this.dataSource.push(`https://placehold.co/400x300/orange/white/png?text=testimage_${i}`)
    }
  }

  build() {
    Column() {
      WaterFlow() {
        Repeat(this.dataSource)
          .virtualScroll()
          .each(() => {
          })
          .key((item) => {
            return item
          })
          .templateId((item) => {
            return 'templateImage'
          })
          .template("templateImage", (ri) => {
            RepeatImageV2({
              src: ri.item,
              attribute: {
                alt: $r('app.media.ic_placeholder_400x300')
              }
            })
              .width('100%')
              .aspectRatio(4 / 3)
          }, { cachedCount: 10 })
      }
      .width('100%')
      .height('100%')
      .cachedCount(0)
      .columnsTemplate('1fr 1fr')
      .rowsTemplate('1fr 1fr')
      .columnsGap(3)
      .rowsGap(3)
    }
  }
}


@ComponentV2
export struct RepeatImageV2 {
  @Param @Require src: PixelMap | ResourceStr | DrawableDescriptor
  @Param attribute: ZZImageModifier = {};
  @Local isAppear: boolean = false

  build() {
    if (this.isAppear) {
      this.imageContent()
    } else {
      this.imageContent()
    }
  }

  @Builder
  imageContent() {
    Image(this.src)
      .draggable(false)
      .alt(this.attribute?.alt)
      .objectFit(this.attribute?.objectFit)
      .borderRadius(this.attribute?.borderRadius)
      .borderColor(this.attribute?.borderColor)
      .width('100%')
      .height('100%')
  }

  @Monitor("src")
  updateImageSrc() {
    this.isAppear = !this.isAppear;
  }
}

export interface ZZImageModifier {
  objectFit?: ImageFit
  alt?: string | Resource | PixelMap
  borderRadius?: Length | BorderRadiuses | LocalizedBorderRadiuses
  borderColor?: ResourceColor | EdgeColors | LocalizedEdgeColors
}

<think>嗯,用户想在HarmonyOS Next中实现列表的上拉加载更多功能。首先,我需要回忆一下HarmonyOS开发文档和相关组件。记得在之前的引用中看到,Scroll容器有下拉触底的函数,这可能和上拉加载有关。比如,引用[2]提到使用Scroll容器的下拉触底函数来实现加载更多。所以可能需要用到onReachEnd事件。 接下来,用户可能需要具体的步骤和代码示例。首先,应该导入必要的组件,比如Scroll和PullToRefresh,不过根据引用[1],PullToRefresh是第三方库,用于下拉刷新,但上拉加载可能直接在Scroll里处理。所以可能需要结合两者,或者单独使用Scroll的触底事件。 然后,数据加载的逻辑是关键。用户需要维护一个数据列表和当前页码,当触发上拉时,页码增加,获取新数据并追加到列表。同时,加载状态和错误处理也很重要,比如显示“加载中”或“加载失败”的提示。 还需要考虑性能优化,比如防抖处理,避免频繁触发加载。另外,列表项的渲染要高效,可能用到ForEach循环和条件判断。 最后,测试和效果预览是必要的,确保功能正常,并且用户体验流畅。可能还需要参考官方文档确认API的用法,确保代码的正确性。现在把这些思路整理成步骤,并提供示例代码,应该就能帮助用户实现了。</think>在 HarmonyOS Next 中实现列表上拉加载更多功能,可以通过以下步骤实现(结合系统提供的 Scroll 容器特性): ### 一、核心实现步骤 1. **使用 Scroll 容器** 在布局文件中使用 Scroll 容器作为列表的父组件,通过其 `onReachEnd` 事件监听触底操作[^2] ```typescript Scroll() { // 列表内容 } .onReachEnd(() => { this.loadMoreData() // 触底时触发加载 }) .width('100%') ``` 2. **数据加载逻辑** ```typescript @State pageIndex: number = 1 @State isLoading: boolean = false loadMoreData() { if (this.isLoading) return this.isLoading = true // 模拟异步请求 setTimeout(() => { // 追加新数据 this.dataList = this.dataList.concat(newData) this.pageIndex++ this.isLoading = false }, 1000) } ``` 3. **加载状态提示** ```typescript if (this.isLoading) { Text('加载中...') .fontSize(12) .textAlign(TextAlign.Center) } ``` ### 二、完整示例代码 ```typescript @Entry @Component struct ListPage { @State dataList: string[] = ['Item 1', 'Item 2', 'Item 3'] @State pageIndex: number = 1 @State isLoading: boolean = false build() { Column() { Scroll() { Column() { ForEach(this.dataList, (item: string) => { Text(item) .fontSize(18) .padding(10) }) if (this.isLoading) { Text('加载中...') .fontSize(12) .textAlign(TextAlign.Center) } } } .onReachEnd(() => { this.loadMoreData() }) } } loadMoreData() { if (this.isLoading) return this.isLoading = true // 模拟API请求 setTimeout(() => { const newData = Array.from({length: 5}, (_, i) => `Item ${this.dataList.length + i + 1}`) this.dataList = this.dataList.concat(newData) this.pageIndex++ this.isLoading = false }, 1500) } } ``` ### 三、优化建议 1. **防抖处理**:通过设置标志位防止重复请求 2. **错误处理**:添加加载失败状态提示 3. **性能优化**:使用 `LazyForEach` 代替 `ForEach` 处理长列表 4. **分页控制**:当无更多数据时显示提示信息
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

冉冉同学_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值