首页 > 其他分享 >鸿蒙HarmonyOS开发:@Observed装饰器和@ObjectLink装饰器:嵌套类对象属性变化

鸿蒙HarmonyOS开发:@Observed装饰器和@ObjectLink装饰器:嵌套类对象属性变化

时间:2024-07-23 17:25:48浏览次数:9  
标签:ObjectLink 装饰 Observed item 组件 class

文章目录

一、装饰器

  • @State装饰器:组件内状态
  • @Prop装饰器:父子单向同步
  • @Link装饰器:父子双向同步
  • @Provide装饰器和@Consume装饰器:与后代组件双向同步

上文所述的装饰器仅能观察到第一层的变化,但是在实际应用开发中,应用会根据开发需要,封装自己的数据模型。对于多层嵌套的情况,比如二维数组,或者数组项class,或者class的属性是class,他们的第二层的属性变化是无法观察到的。这就引出了@Observed/@ObjectLink装饰器。

二、概述

@ObjectLink和@Observed类装饰器用于在涉及嵌套对象或数组的场景中进行双向数据同步:

被@Observed装饰的类,可以被观察到属性的变化;

子组件中@ObjectLink装饰器装饰的状态变量用于接收@Observed装饰的类的实例,和父组件中对应的状态变量建立双向数据绑定。这个实例可以是数组中的被@Observed装饰的项,或者是class object中的属性,这个属性同样也需要被@Observed装饰。

@Observed用于嵌套类场景中,观察对象类属性变化,要配合自定义组件使用,如果要做数据双/单向同步,需要搭配@ObjectLink或者@Prop使用。

三、限制条件

使用@Observed装饰class会改变class原始的原型链,@Observed和其他类装饰器装饰同一个class可能会带来问题。

@ObjectLink装饰器不能在@Entry装饰的自定义组件中使用。

四、装饰器说明

@Observed类装饰说明
装饰器参
类装饰器装饰class。需要放在class的定义前,使用new创建类对象。
@ObjectLink变量装饰器说明
装饰器参数
允许装饰的变量类型必须为被@Observed装饰的class实例,必须指定类型。
不支持简单类型,可以使用@Prop。
支持继承Date、Array的class实例,API11及以上支持继承Map、Set的class实例。
@ObjectLink的属性是可以改变的,但是变量的分配是不允许的,也就是说这个装饰器装饰变量是只读的,不能被改变。
被装饰变量的初始值不允许。

五、Toggle组件

组件提供勾选框样式、状态按钮样式及开关样式。

1、子组件

仅当ToggleType为Button时可包含子组件。

2、接口

Toggle(options: { type: ToggleType, isOn?: boolean })

  • 参数
参数名参数类型必填参数描述
typeToggleType开关的样式。
默认值:ToggleType.Switch。
isOnboolean开关是否打开,true:打开,false:关闭。
默认值:false
从API version 10开始,该参数支持$$双向绑定变量。
3、ToggleType枚举
名称描述
Checkbox提供单选框样式。
Button提供状态按钮样式,如果子组件有文本设置,则相应的文本内容会显示在按钮内部。
默认尺寸为:高为28vp,宽无默认值。
Switch提供开关样式。
4、事件

开关状态切换时触发该事件。

onChange(callback: (isOn: boolean) => void)

  • 参数
参数名类型必填说明
isOnboolean为true时,代表状态从关切换为开。false时,代表状态从开切换为关。

六、示例演示

本示例演示联系人管理功能页面。
可以新增,删除,收藏,展开手机号登功能。
使用到的组件间相互通信功能
@Prop装饰器:父子单向同步
@Link装饰器:父子双向同步
@Watch装饰器监听状态变量的变化
@Observed装饰器和@ObjectLink装饰器:嵌套类对象属性变化
@Extend装饰器:定义扩展组件样式
Toggle组件

1、代码
// 定义变量ID,默认从1开始,自增
let id = 1

// @Observed装饰的类
@Observed
class Person {
  name: string
  phone: string
  isCollect: boolean = false
  id: number

  constructor(name: string, phone: string) {
    // id自增
    this.id = id++
    this.name = name
    this.phone = phone
  }
}

// 随机姓名方法,为了演示简写
function getRandomName() {
  return "张三" + Math.floor(Math.random() * 100)
}

//随机手机号,为了演示简写
function getRandomPhone() {
  return "1" + (Math.floor(Math.random() * 9000000000) + 1000000000)
}

@Entry
@Component
struct ContactsList {
  // 联系人列表
  @State contactsList: Person[] = [new Person(getRandomName(), getRandomPhone()), new Person(getRandomName(), getRandomPhone())]
  // 当前展开的联系人ID
  @State currentExpandID: number = 0
  // 是否点击选中
  @State isSelect: boolean = false
  // 选中的联系人ID列表
  @State selectIdList: number[] = []

  build() {
    Column() {
      Row({ space: 10 }) {
        Text("联系人管理")
          .fontSize(20)
        Blank()
        Button(this.isSelect ? "取消" : "选中")
          // 扩展组件样式
          .globalButtonStyle(Color.Gray)
            // 点击切换选中/取消按钮事件
          .onClick(() => {
            this.isSelect = !this.isSelect
            this.selectIdList = []
          })
        Button("+")
          // 扩展组件样式
          .globalButtonStyle(Color.Gray)
          .onClick(() => {
            // 新增联系人
            this.contactsList.push(new Person(getRandomName(), getRandomPhone()))
          })
      }
      .width("100%")
      .padding(10)

      List({ space: 10 }) {
        ForEach(this.contactsList, (item: Person) => {
          ListItem() {
            // 观察对象类属性变化,要配合自定义组件使用
            ContactsItem({ item,
              currentExpandID: $currentExpandID,
              isSelect: this.isSelect,
              selectIdList: $selectIdList })
          }
        })

      }.padding(10)
      .layoutWeight(1)

      if (this.isSelect) {
        Button("删除")
          .margin(10)
            // 扩展组件样式
          .globalButtonStyle(Color.Red)
            // 删除按钮点击事件
          .onClick(() => {
            this.contactsList = this.contactsList.filter((item: Person) => {
              return !this.selectIdList.includes(item.id)
            })
          })
      }

    }
    .width('100%')
    .height('100%')
    .backgroundColor("#f7f7f7")
  }
}

// 自定义组件
@Component
struct ContactsItem {
  // 数据双向同步,需要搭配@ObjectLink
  @ObjectLink item: Person
  // 组件内状态
  @State isExpand: boolean = false
  // 父子双向同步 监听状态变化
  @Link @Watch("onChangeCurrentID") currentExpandID: number
  // 父子单向同步
  @Prop isSelect: boolean
  // 父子双向同步
  @Link selectIdList: number[]

  // 监听状态变量变化回调
  onChangeCurrentID() {
    if (this.item.id != this.currentExpandID) {
      this.isExpand = false
    }
  }

  build() {
    Column() {
      Row({ space: 10 }) {
        // 复选框
        if (this.isSelect) {
          Toggle({ type: ToggleType.Checkbox })
            // 选中状态发生变化时的事件
            .onChange((val) => {
              if (val) {
                this.selectIdList.push(this.item.id)
              } else {
                let index: number = this.selectIdList.indexOf(this.item.id)
                this.selectIdList.splice(index, 1)
              }
            })
        }
        // 头像
        Image($r("app.media.picture"))
          .width(30)
          .height(30)
        // 姓名
        Text(this.item.name)
          .fontSize(18)

        Blank()
        // 收藏
        Image(this.item.isCollect ? $r("app.media.collect_select") : $r("app.media.collect"))
          .width(26)
          .height(26)
          .onClick(() => {
            // 第二层的属性值变化
            this.item.isCollect = !this.item.isCollect
          })
      }
      .height(40)
      .width("100%")

      // 手机号
      if (this.isExpand) {
        Divider()
          .margin({ top: 10, bottom: 6 })
          .color("#EEEEEE")

        Row() {
          Text("手机号码:" + this.item.phone)
            .fontSize(16)
        }
        .height(30)
        .width("100%")
      }
    }
    .width("100%")
    .padding(10)
    .borderRadius(6)
    .backgroundColor(Color.White)
    // 点击展开显示手机号码
    .onClick(() => {
      this.isExpand = !this.isExpand
      this.currentExpandID = this.item.id
    })
  }
}

// 定义扩展组件样式
@Extend(Button) function globalButtonStyle(color: Color) {
  .height(30)
  .fontSize(14)
  .backgroundColor(color)
}
2、效果
  • 点击新增按钮,可以新增联系人
  • 点击收藏按钮,可以切换是否收藏
    在这里插入图片描述
  • 点击联系人,可以展开当前联系人,查看手机号码
  • 点击上面选择按钮,可以切换选择页面
  • 点击联系人前面复选框,可以选中联系
  • 点击删除按钮,可以删除选中的联系人
    在这里插入图片描述

标签:ObjectLink,装饰,Observed,item,组件,class
From: https://blog.csdn.net/shanghai597/article/details/140472486

相关文章

  • 闭包与装饰器
    闭包定义闭包函数是指能够访问另一个函数作用域中变量的函数。当内部函数可以访问它们所在的外部函数中声明的所有局部变量、参数和其他内部函数时,如果其中一个这样的内部函数在包含它们的外部函数之外被调用,就会形成闭包,外部函数能将内部函数返回,内部函数能够访问内部函......
  • python进阶---闭包与装饰器
    一、闭包        在Python中,闭包是指一个函数内部定义的函数,这个内部函数可以访问并修改其外部函数的局部变量,即使外部函数已经执行完毕。闭包可以通过多层函数嵌套来实现。    闭包的三要素:    1、外部函数嵌套内部函数    2、外部函数返......
  • python——闭包与装饰器
    闭包与装饰器1.闭包 定义闭包的三要素:外部函数嵌套内部函数外部函数将内部函数返回内部函数可以访问外部函数的局部变量了解三要素之后就可以定义一个闭包了deffun1(x):#外部函数嵌套fun2deffun2():print(x)#内部函数访问外部函数的局部变量ret......
  • Python—装饰器
    Python装饰器(decorators)是一种强大且灵活的特性,它允许在不修改原有函数代码的情况下,为函数增加新的功能。装饰器本质上是一个函数,它可以接收一个函数作为参数,并返回一个新的函数。这个新函数是对原函数的一种包装或增强。一、示例详解importrandomimporttimedata=[ra......
  • Python 装饰器 详解+案例
    Python装饰器是一种特殊的函数,用于修改其他函数的功能。装饰器可以在不改变原函数代码的情况下,对函数进行增加、修改或者扩展功能。装饰器的语法形式是在函数定义前使用@符号,并在@后面加上装饰器的名称。装饰器函数接受被装饰函数作为参数,并返回一个修改后的函数。impo......
  • 博文标题:探索Python中的元编程:装饰器的魔法
    引言在Python的世界里,装饰器(Decorators)是一种非常强大的特性,它允许程序员在不修改原始函数代码的情况下,为函数添加新的功能。这种机制不仅增强了代码的可读性和可维护性,还提供了高度的灵活性和扩展性。本文将深入探讨装饰器的基本概念、工作原理以及如何利用它们来简化和......
  • 如何在Python中使用装饰器动态创建类方法?
    我正在开发一个Python项目,我需要在运行时动态地为类创建方法。我想使用装饰器根据一些外部配置将这些方法添加到类中。要求是:装饰器应该从外部配置(例如字典)读取方法定义。装饰器应该动态地将这些方法添加到类中。每个生成的方法都应具有配置中指定的自己唯一的实现。以......
  • 千字长文讲解python装饰器
    1.装饰器是一个用于封装函数或类的代码的工具。它显式的将封装器应用到函数或类上,从而使它们选择加入到装饰器的功能中【装饰器不仅可以装饰函数,也可以装饰类】。2.对于①在函数运行前处理常见前置条件(例如确认授权),或②在函数运行后确保清理(例如输出清除或异常处理)装饰器都......
  • Python中的`@property`装饰器:深入解析与实战应用
    Python中的@property装饰器:深入解析与实战应用在Python中,@property装饰器是一种强大的工具,它允许类的方法被当作属性来访问。这一特性极大地增强了类的封装性和易用性,使得类的外部使用者可以像访问普通属性一样访问由方法计算或处理过的数据,而无需直接调用这些方法。本文将......
  • 八、函数高级、装饰器
    文章目录学习目标一、递归函数二、匿名函数三、列表相关的一些方法3.1sort与sorted方法3.2filter内置类3.3map内置类3.4reduce四、常用内置函数总结五、高阶函数5.1函数的嵌套5.2闭包的概念六、装饰器6.1计算一段代码的执行时间6.2优化......