【HarmonyOS】 多层嵌套对象通过@ObjectLink和@Observed实现渲染更新处理!

【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;
  }
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值