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
Repeat与@Builder混用
当Repeat与@Builder混用时,必须将RepeatItem类型整体进行传参,组件才能监听到数据变化,如果只传递RepeatItem.item或RepeatItem.index,将会出现UI渲染异常。
更多关于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时的缓存机制问题。关键点如下:
- 问题根源在于virtualScroll配置和缓存机制冲突:
- 你设置了
virtualScroll({totalCount})
但每次添加数据后没有更新totalCount
Repeat
的key()
函数虽然正确使用了id,但虚拟滚动可能未正确感知数据变化
- 解决方案建议:
- 每次调用
addTestData()
后需要同步更新virtualScroll
的totalCount
:
this.imageList.push(...moreArr)
this.imageList.length // 需要同步更新virtualScroll的totalCount
- 其他优化点:
- 检查
templateId()
函数返回的类型值是否唯一稳定 - 确保
cachedCount(6)
与virtualScroll
配置协调
这种现象在V2版本中更明显是因为:
- V2的
@Trace
/@ObservedV2
响应式机制更严格 Repeat
相比LazyForEach
对虚拟滚动的实现有差异- 虚拟滚动在复用Item时对数据变化的敏感性更高
建议先尝试更新totalCount
,如果问题依旧可能需要调整缓存策略或检查模板ID的稳定性。