【HarmonyOS】 多层嵌套对象通过@ObjectLink和@Observed实现渲染更新处理!
一、问题背景:
上文讲过 (【HarmonyOS】List组件多层对象嵌套ForEach渲染更新的处理)对多层嵌套的简单处理,即:深拷贝item数据,该场景适用于简单的数据源处理。
但是若结构对象层级嵌套很多,属性量级大。使用深拷贝的方法就显得得不偿失了。
那应该怎么处理呢?其实还可以拆分大的数据源对象,拆薄。新增@State修饰的状态变量控制刷新。
不过这种方案,对于历史业务逻辑开发是不友好的,因为我们的历史业务数据结构是固定,不方便拆分。
二、解决方案:
花开两朵,各表一枝。在以上方案都不能解决的情况下,官网推荐了一种方式,可以无感知的实现,多层嵌套的对象属性变化,就刷新渲染对应的列表UI。
该方案唯一的缺点就是需要对于item view需进行组件Component申明,需要将list包裹的item UI进行拆分剥离。
实现该方案效果需要以下详细步骤:
1.对嵌套的数据结构类,进行@Observed修饰
2.item UI拆分剥离为组件Component
3.数据源item数据在Component组件中使用@ObjectLink监听变化,以便于通知给父组件的@State修饰的数据源列表数据。
以上步骤完成后,调用item数据对象直接修改任意层级属性值,列表就会同步更新渲染。
三、DEMO示例:
DEMO讲解通过注释的方式表明。若有不清楚的点,可私信我沟通。
import { util } from '@kit.ArkTS';
/**
* 三级数据结构
*/
// 每一级数据结构都需要用Observed修饰
class GrandsonInfo {
content: string = "";
}
/**
* 二级数据结构
*/
// 每一级数据结构都需要用Observed修饰
class ChildInfo {
index: number;
grandsonInfo: GrandsonInfo;
constructor(index: number, content: string) {
this.index = index;
this.grandsonInfo = new GrandsonInfo();
this.grandsonInfo.content = content;
}
}
/**
* 一级数据结构
*/
// 每一级数据结构都需要用Observed修饰
class ItemInfo {
key: string = util.generateRandomUUID(true);
name: string;
icon: Resource;
childInfo: ChildInfo;
select: boolean;
constructor(name: string, icon: Resource, index: number, content: string) {
this.name = name;
this.icon = icon;
this.childInfo = new ChildInfo(index, content);
this.select = false;
}
}
/**
* 多层嵌套刷新渲染
*/
struct ObservedPage {
private TAG: string = "ObservedPage";
mListData: Array<ItemInfo> = [];
aboutToAppear(): void {
this.mListData.push(new ItemInfo('游戏', $r("app.media.iconA"), 1, "鹅厂1"));
this.mListData.push(new ItemInfo('游戏', $r("app.media.iconB"), 2, "鹅厂2"));
this.mListData.push(new ItemInfo('游戏', $r("app.media.iconA"), 3, "鹅厂3"));
this.mListData.push(new ItemInfo('游戏', $r("app.media.iconB"), 4, "鹅厂4"));
this.mListData.push(new ItemInfo('游戏', $r("app.media.iconA"), 5, "鹅厂5"));
this.mListData.push(new ItemInfo('游戏', $r("app.media.iconB"), 6, "鹅厂6"));
}
build() {
List() {
ForEach(this.mListData, (item: ItemInfo, index: number) => {
ListItem() {
// ListItem包裹的ItemView需要抽离成Component组件的形态,参数通过属性赋值传递,即:大括号包裹中,属性值key value形式赋值
ItemView({
item: item,
index: index
})
}
}, (item: ItemInfo) => JSON.stringify(item)) // , (item: ItemInfo) => JSON.stringify(item)
// keyGenerator: ArkUI框架会对重复的键值发出警告。在UI更新的场景下,如果出现重复的键值,框架可能无法正常工作. [渲染异常]
// 除非必要,否则不推荐将第三个参数KeyGenerator函数处于缺省状态,应尽量避免最终键值生成规则中包含index。[渲染性能降低]
}
.width("100%")
.height("100%")
.padding({ left: px2vp(60), right: px2vp(60) })
}
}
struct ItemView {
private TAG: string = "ItemView";
index: number = 0;
// 列表数据的单个item对象数据,需要使用ObjectLink修饰监听,用于将数据变化传递给外部父组件的mListData
item: ItemInfo
build() {
Row() {
Image(this.item.icon)
.width(px2vp(200))
.height(px2vp(200))
Text(this.item.name + "(" + this.item.childInfo.index + ")" + " [ " + this.item.childInfo.grandsonInfo.content + " ] ")
.fontSize(px2fp(52))
Blank()
if(this.isLog(this.item, this.index)){
if(this.item.select){
Image($r("app.media.icon_check"))
.size({
width: px2vp(72),
height: px2vp(72)
})
}
}
}
.width('100%')
.justifyContent(FlexAlign.Start)
.onClick(()=>{
this.item.select = !this.item.select;
if(this.item.select){
// 使用很方便,只需要直接改变item数据的任意层级属性值,变化就会同步刷新
this.item.childInfo.index = 666;
this.item.childInfo.grandsonInfo.content = "鹅厂23333"
}else{
this.item.childInfo.index = this.index;
this.item.childInfo.grandsonInfo.content = "鹅厂" + this.index;
}
console.log(this.TAG, " ItemView onClick: " + this.index + " item.select: " + this.item.select);
})
}
private isLog(item: ItemInfo, index: number){
console.log(this.TAG, " ItemView isLog index: " + index + " item.select: " + item.select);
return true;
}
}