鸿蒙@Observed+@ObjectLink更新UI失效

66 阅读1分钟

先上问题代码:

@Observed
class Message {
    title: string = ''
    id: string = ''
}

@Component
struct MessageCard {
    @ObjectLink message: Message

    build() {
        Column() {
            Text(this.message.title).onClick(() => {
                this.message.title = Math.random() + ''
            })
        }.backgroundColor(Color.Orange)
    }
}


class MyDataSource implements IDataSource {
    data: Array<Message> = []
    listener?: DataChangeListener

    totalCount(): number {
        return this.data.length
    }

    getData(index: number): Message {
        return this.data[index]
    }

    registerDataChangeListener(listener: DataChangeListener): void {
        this.listener = listener
    }

    unregisterDataChangeListener(listener: DataChangeListener): void {
        this.listener = undefined
    }
}

@Entry
@Component
struct Index {
    dataSource: MyDataSource = new MyDataSource()
    version = 0
    msgId: number = 0

    aboutToAppear(): void {
        let msgs = this.buildMessages(this.version)
        this.dataSource.data = msgs
    }

    build() {
        Column() {
            Button('刷新第一个').onClick(() => {
                animateTo({ duration: 300 }, () => {
                    this.dataSource.data[0].title = '阿西吧嗷嗷' + Math.random()
                    this.dataSource.listener?.onDataChange(0)
                })
            })
            Button('刷新全部').onClick(() => {
                this.dataSource.data = this.buildMessages(++this.version)
                this.dataSource.listener?.onDataReloaded()
            })
            List({ space: 20 }) {
                ListItemGroup() {
                    LazyForEach(this.dataSource, (item: Message, index: number) => {
                        ListItem() {
                            MessageCard({
                                message: item
                            })
                        }
                    }, (item: Message, index: number) => {
                        return item.id
                    })
                }.divider({ strokeWidth: 20 })
            }.width('100%').height('auto').layoutWeight(1)
        }.width('100%').height('100%').margin({ top: 100 })
    }

    buildMessages(version:
        number
    ) {
        let msgs = new Array<Message>()
        for (let index = 0; index < 10; index++) {
            msgs.push({
                title: `msg${index} version: ${version}`,
                id: this.msgId++ + '',
            })
        }
        return msgs
    }
}

此demo中,实现了两个可以刷新数据的按钮和一个List+LazyForEach的列表,用@Observed+@ObjectLink驱动UI刷新

问题:实际只有通过dataSource的onDataReload()更新全部数据才能刷新UI,直接更新单个数据的属性UI刷新是失效的,调onDataChange也不行。

原因:

调用dataSource的onDataReload()更新全部数据因为生成的Key不一样所以直接把所有的列表项UI销毁重建了,所以能刷新UI。但直接更新列表项数据的属性不刷新UI,调了onDataChange也不行,是因为创建列表数据项时是通过字面量创建的,没有用new关键字,一定要用new,所以框架在编译时没有生成监听器。

运行时系统发出了警告:

image.png

排查问题的时候就很纳闷,明明是有@Observed注解的,但就是没有生效,正常如果没有@Observed的话与@ObjectLink搭配使用编译时过不去的。

踩这个坑的时候我是因为这个对象的属性太多用字面量创建比较省事,大家共勉