【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: ItemInfobuild() {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;}
}