【HarmonyOS NEXT】@Observed和@ObjectLink嵌套对象属性更改UI不刷新问题

149 阅读3分钟

【HarmonyOS NEXT】@Observed和@ObjectLink嵌套对象属性更改UI不刷新问题

一、问题背景

使用了@Observed和@ObjectLink,修改嵌套对象的属性,UI还是不刷新,常见的问题有以下三种形式: 1.多级嵌套,嵌套对象的类并没有添加@Observed进行监听 2.多级嵌套,嵌套对象的View组件没有抽离出来,添加@ObjectLink进行该级对象的监听绑定 3.嵌套对象,并没有new出来创建,直接赋值没有创建对象的过程,无法激活Observed监听

二、代码举例

以代码示例举例: 1.创建了接口TestInfoInterFace ,父类TestInfo,嵌套类TestItem 。

interface TestInfoInterFace {
  name: string;
  items: TestItem[];
}

class TestItem {
  content: string = "";
  isClicked: boolean = false;
}

@Observed
class TestInfo {
  name: string;
  items: TestItem[];

  constructor(name: string, items: TestItem[]) {
    this.name = name;
    this.items = items;
  }
}

2.添加测试数据,渲染列表,单元格数据基本类型结构为TestInfo。

@Entry
@Component
struct TestPage {
  @State mTestDataArr: TestInfo[] = [
    new TestInfo('测试数据1', [{
      content: '单元数据1',
      isClicked: false
    }, {
      content: '单元数据1',
      isClicked: false
    }]),
    new TestInfo('测试数据2', [{
      content: '单元数据1',
      isClicked: false
    }, {
      content: '单元数据1',
      isClicked: false
    }]),
    new TestInfo('测试数据3', [{
      content: '单元数据1',
      isClicked: false
    }, {
      content: '单元数据1',
      isClicked: false
    }]),
  ]

  build() {
    Column() {
      ForEach(this.mTestDataArr, (item: TestInfoInterFace) => {
        ChildView({
          mTestInfo: item
        })
      })
    }
    .width('100%')
    .height('100%')
  }
}

3.抽离嵌套组件ChildView ,绑定双向监听。


@Component
export struct ChildView {
  private TAG: string = "TestPage";
  @ObjectLink mTestInfo: TestInfo

  build() {
    Column() {
      Text(this.mTestInfo.name)
        .backgroundColor(Color.Red)
        .fontSize(px2fp(52))

      ForEach(this.mTestInfo.items, (tempInfo: TestItem) => {
        Text(tempInfo.content)
          .fontSize(px2fp(52))
          .backgroundColor(tempInfo.isClicked ? Color.Blue : Color.Yellow)
          .onClick(() => {
            tempInfo.isClicked = !tempInfo.isClicked
            console.log(this.TAG, JSON.stringify(tempInfo))
          })
      })

      Divider()
    }
  }
}

渲染界面后的效果为: 在这里插入图片描述 此时我们点击单元数据1或者2,去修改isClicked选中状态,并不会刷新UI,整个代码有以上总结的三个问题: 1.TestItem 多级嵌套,嵌套对象的类并没有添加@Observed进行监听

2.ChildView 多级嵌套了一个层级,直接就进行了循环渲染,其嵌套对象的View组件没有抽离出来,添加@ObjectLink进行该级对象的监听绑定

3.mTestDataArr嵌套对象中的TestItem并没有new出来创建,是通过花括号直接赋值没有创建对象的过程,无法激活Observed监听

三、完整DEMO示例:

interface TestInfoInterFace {
  name: string;
  items: TestItem[];
}

// TODO 问题1:多层级时,需要逐个层级进行类监听
@Observed
class TestItem {
  content: string = "";
  isClicked: boolean = false;

  constructor(content: string, isClicked: boolean) {
    this.content = content;
    this.isClicked = isClicked;
  }
}

@Observed
class TestInfo {
  name: string;
  items: TestItem[];

  constructor(name: string, items: TestItem[]) {
    this.name = name;
    this.items = items;
  }
}


@Entry
@Component
struct TestPage {
  // TODO 问题3 每个被设置Observed的对象,需要new出来创建,才能激活监听,花括号的形式赋值,并不会激活监听。
  @State mTestDataArr: TestInfo[] = [
  new TestInfo('测试数据1', [new TestItem('单元数据1', false), new TestItem('单元数据2', false)]),
  new TestInfo('测试数据2', [new TestItem('单元数据1', false), new TestItem('单元数据2', false)]),
  new TestInfo('测试数据3', [new TestItem('单元数据1', false), new TestItem('单元数据2', false)]),
  //   new TestInfo('测试数据1', [{
  //     content: '单元数据1',
  //     isClicked: false
  //   }, {
  //     content: '单元数据1',
  //     isClicked: false
  //   }]),
  //   new TestInfo('测试数据2', [{
  //     content: '单元数据1',
  //     isClicked: false
  //   }, {
  //     content: '单元数据1',
  //     isClicked: false
  //   }]),
  //   new TestInfo('测试数据3', [{
  //     content: '单元数据1',
  //     isClicked: false
  //   }, {
  //     content: '单元数据1',
  //     isClicked: false
  //   }]),
  ]

  build() {
    Column() {
      ForEach(this.mTestDataArr, (item: TestInfoInterFace) => {
        ChildView({
          mTestInfo: item
        })
      })
    }
    .width('100%')
    .height('100%')
  }
}

@Component
export struct ChildView {
  private TAG: string = "TestPage";
  @ObjectLink mTestInfo: TestInfo

  build() {
    Column() {
      Text(this.mTestInfo.name)
        .backgroundColor(Color.Red)
        .fontSize(px2fp(52))

      // TODO 多层级时,需要逐个层级进行剥离,创建子组件和绑定双向监听。
      // ForEach(this.mTestInfo.items, (tempInfo: TestItem) => {
      //   Text(tempInfo.content)
      //     .fontSize(px2fp(52))
      //     .backgroundColor(tempInfo.isClicked ? Color.Blue : Color.Yellow)
      //     .onClick(() => {
      //       tempInfo.isClicked = !tempInfo.isClicked
      //       console.log(this.TAG, JSON.stringify(tempInfo))
      //     })
      // })

      ForEach(this.mTestInfo.items, (tempInfo: TestItem) => {
        ItemView({
          mItem: tempInfo
        }).margin({
          top: px2vp(100)
        })
      })

      Divider()
    }
  }
}

@Component
export struct ItemView {
  private TAG: string = "TestPage";
  @ObjectLink mItem: TestItem

  build() {
    Text(this.mItem.content)
      .fontSize(px2fp(52))
      .backgroundColor(this.mItem.isClicked ? Color.Blue : Color.Yellow)
      .onClick(() => {
        this.mItem.isClicked = !this.mItem.isClicked
        console.log(this.TAG, JSON.stringify(this.mItem))
      })
  }
}