HarmonyOS鸿蒙Next中Repeat新增item后导致列表界面的Item重复的问题

HarmonyOS鸿蒙Next中Repeat新增item后导致列表界面的Item重复的问题

这个是数据项类型:

@ObservedV2
export class ItemDataV2 {

  @Trace
  id: number;

  @Trace
  type:string = '';

  @Trace
  title: ResourceStr;

  @Trace
  img: Resource;

  constructor(id: number, title: ResourceStr, img: Resource, type?:string) {
    this.id = id;
    this.title = title;
    this.img = img;
    if(type){
      this.type = type
    }
  }
}

这个是列表:

@ComponentV2
export struct RepeatDemo {
  @Local imageList: Array<ItemDataV2> = this.getFirstPageData()

 //模拟的第一页的数据
  getFirstPageData(): Array<ItemDataV2> {
    let imageList: Array<ItemDataV2> = []
    //第一项轮播
    imageList.push(new ItemDataV2(0, $r('app.string.empty'), $r('app.media.empty'), 'Swiper'))
    //第二项grid
    imageList.push(new ItemDataV2(1, $r('app.string.empty'), $r('app.media.empty'), 'Grid'))
    imageList.push(...getItemData(2, 10))
    return imageList
  }

  build() {
    Column() {
      Button('addItem').onClick(() => { //点击按钮添加一页的数据
        this.addTestData()
      })

      List({ space: 10 }) {
        Repeat<ItemDataV2>(this.imageList)
          .each((ri: RepeatItem<ItemDataV2>) => {
            ListItem() {
              this.imageItemView(ri.item)
            }
            .opacity(printItem(ri))//打印item
          })
          .key((item: ItemDataV2) => item.id.toString())
          .templateId((item: ItemDataV2) => {
            return item.type
          })
          .template('Swiper', (_: RepeatItem<ItemDataV2>) => {
            ListItem() {
              this.ListSwiperHeader()//轮播
            }
          }, { cachedCount: 1 })
          .template('Grid', (_: RepeatItem<ItemDataV2>) => {
            ListItem() {
              this.ListGridHeader()//网格
            }
          }, { cachedCount: 1 })
          .virtualScroll({ totalCount: this.imageList.length })
      }
      .cachedCount(6)
      .width('100%')
      .padding(15)
    }
  }

  //添加一页的数据
  addTestData(): void {
    let count = this.imageList.length
    Logger.warn(`loadMoreData count = ${count}`)
    let moreArr: ItemDataV2[] = getItemData(count, 10)
    this.imageList.push(...moreArr)
  }

  [@Builder](/user/Builder)
  imageItemView(item: ItemDataV2) {
    Stack() {
      Image(item.img)
        .objectFit(ImageFit.Cover)
        .aspectRatio(3)
        .borderRadius(12)

      Text(item.title)
        .padding(15)
        .fontSize(30)
        .fontWeight(FontWeight.Bold)
        .fontColor(Color.Black)

    }.alignContent(Alignment.TopStart)
    .width('100%')
    .opacity(printItem(item))//[@Builder](/user/Builder) 里面打印item
  }
}

这个是用于模拟的数据:

//打印数据
function printItem(o: object): number {
  Logger.warn(`printItem ${JSON.stringify(o).replaceAll('"__ob_', '"')}`)
  return 1
}

let swiperImg: Array<Resource> =
  [$r('app.media.fig1'), $r('app.media.fig2'), $r('app.media.fig3'), $r('app.media.fig4')]
//创建模拟的数据
function getItemData(start: number, count: number): ItemDataV2[] {
  let arr: ItemDataV2[] = []
  for (let i = 0; i < count; i++) {
    let imageIndex = i % swiperImg.length
    arr.push(new ItemDataV2(i + start, (i + start).toString(), swiperImg[imageIndex]))
  }
  return arr
}

结果发现点击按钮多添加几页的数据后,滑动到第三页的数据就开始重复了:

@Builder里面加了打印发现: 滑动超过2页后,@Builder里面就没有打印了,后面的item就都没有渲染了,感觉是这个list一直在复用前面这20个item 然后在ListItem() { this.imageItemView(ri.item) } .opacity(printItem(ri))加了打印,结果发现滑倒后面后这里的item全部打印了,然而@Builder里面只打印了前面20个的item。

之前用V1状态修饰符和lazyForeach没碰到这个问题,后面替换成V2的和Repeat就出问题了,这到底是什么原因?


更多关于HarmonyOS鸿蒙Next中Repeat新增item后导致列表界面的Item重复的问题的实战教程也可以访问 https://www.itying.com/category-93-b0.html

3 回复

Repeat与@Builder混用

当Repeat与@Builder混用时,必须将RepeatItem类型整体进行传参,组件才能监听到数据变化,如果只传递RepeatItem.item或RepeatItem.index,将会出现UI渲染异常。

https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-new-rendering-control-repeat#repeat与builder混用

更多关于HarmonyOS鸿蒙Next中Repeat新增item后导致列表界面的Item重复的问题的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


在HarmonyOS鸿蒙Next中,Repeat组件新增item后导致列表界面Item重复的问题,通常是由于数据源未正确更新或绑定机制未及时响应。确保在新增item后,数据源已同步更新,并触发UI刷新。检查Repeat组件的绑定逻辑,确保数据源与UI组件之间的绑定关系正确。若使用ListContainer或RecycleView,确保适配器的notifyDataSetChanged方法被调用。

从代码和现象分析,这应该是HarmonyOS Next中Repeat组件在虚拟滚动(virtualScroll)模式下复用Item时的缓存机制问题。关键点如下:

  1. 问题根源在于virtualScroll配置和缓存机制冲突:
  • 你设置了virtualScroll({totalCount})但每次添加数据后没有更新totalCount
  • Repeatkey()函数虽然正确使用了id,但虚拟滚动可能未正确感知数据变化
  1. 解决方案建议:
  • 每次调用addTestData()后需要同步更新virtualScrolltotalCount
this.imageList.push(...moreArr)
this.imageList.length // 需要同步更新virtualScroll的totalCount
  1. 其他优化点:
  • 检查templateId()函数返回的类型值是否唯一稳定
  • 确保cachedCount(6)virtualScroll配置协调

这种现象在V2版本中更明显是因为:

  1. V2的@Trace/@ObservedV2响应式机制更严格
  2. Repeat相比LazyForEach对虚拟滚动的实现有差异
  3. 虚拟滚动在复用Item时对数据变化的敏感性更高

建议先尝试更新totalCount,如果问题依旧可能需要调整缓存策略或检查模板ID的稳定性。

回到顶部