HarmonyOS 组件样式@Style 、 @Extend、自定义扩展(AttributeModifier、AttributeUpdater)

1. HarmonyOS @Style 、 @Extend、自定义扩展(AttributeModifier、AttributeUpdater)

@Styles装饰器:定义组件重用样式

;@Extend装饰器:定义扩展组件样式

自定义扩展:AttributeModifierAttributeUpdater

1.1. 区别

1.1.1. 对比

声明式语法引入了@Styles和@Extend两个装饰器,可以解决部分复用的问题,但是存在以下受限场景:

(1)@Styles和@Extend均是编译期处理,不支持跨文件的导出复用。

(2)@Styles仅能支持通用属性、事件,不支持组件特有的属性。

(3)@Styles虽然支持在多态样式下使用,但不支持传参,无法对外开放一些属性。

(4)@Extend虽然能支持特定组件的私有属性、事件,但同样不支持跨文件导出复用。

(5)@Styles、@Extend对于属性设置,无法支持业务逻辑编写,动态决定是否设置某些属性。只能通过三元表达式对所有可能设置的属性进行全量设置,设置大量属性时效率低下。

(为了解决上述问题,ArkUI引入了AttributeModifier的机制,可以通过Modifier对象动态修改属性。能力对比如下:

项目 @Styles @Extend AttributeModifier
跨文件导出 不支持 不支持 支持
通用属性设置 支持 支持 支持
通用事件设置 支持 支持 部分支持
组件特有属性设置 不支持 支持 部分支持
组件特有事件设置 不支持 支持 部分支持
参数传递 不支持 支持 支持
多态样式 支持 不支持 支持
业务逻辑 不支持 不支持 支持

可以看出,AttributeModifier的能力和灵活性更好,当前持续在构建全量的属性、事件设置能力。未来,AttributeModifier可以替代@Styles和@Extend几乎所有的能力,建议上述场景都使用AttributeModifier。

1.1.2. @Style 和 @Extend 是否支持export导出

方舟UI框架(ArkUI)解答文档

(1)@Styles或@Extend目前不支持export导出,后续这两个装饰器不会继续演进。

(2)推荐开发者使用新的样式复用方法,通过attributeModifier属性动态的设置组件,通过自定义class继承对应基础组件的Modifier,在class中设置复用的属性,对应class也没有无法export的限制。但是attributeModifier的复用能力仍有缺陷,目前不支持事件手势,这两个能力已有需求跟踪。

1.2. @Styles装饰器:定义组件重用样式

如果每个组件的样式都需要单独设置,在开发过程中会出现大量代码在进行重复样式设置,虽然可以复制粘贴,但为了代码简洁性和后续方便维护,我们推出了可以提炼公共样式进行复用的装饰器@Styles。

@Styles装饰器可以将多条样式设置提炼成一个方法,直接在组件声明的位置调用。通过@Styles装饰器可以快速定义并复用自定义样式。
**  说明:**

从API version 9开始,该装饰器支持在ArkTS卡片中使用。

从API version 11开始,该装饰器支持在元服务中使用。

1.2.1. 装饰器使用说明

(1)当前@Styles仅支持通用属性和通用事件。

@Styles方法不支持参数,反例如下。

javascript 复制代码
// 反例: @Styles不支持参数
@Styles function globalFancy (value: number) {
  .width(value)
}

(2)@Styles可以定义在组件内或全局,在全局定义时需在方法名前面添加function关键字,组件内定义时则不需要添加function关键字。
**  说明**

只能在当前文件内使用,不支持export。

如果想实现export功能,推荐使用AttributeModifier

javascript 复制代码
// 全局
@Styles function functionName() { ... }

// 在组件内
@Component
struct FancyUse {
  @Styles fancy() {
    .height(100)
  }
}

(3)如果要实现跨文件操作的功能,可以参考使用动态属性设置。

javascript 复制代码
// index.ets
import { MyButtonModifier } from './setAttribute'

@Entry
@Component
struct attributeDemo {
  @State modifier: MyButtonModifier = new MyButtonModifier()

  build() {
    Row() {
      Column() {
        Button("Button")
          .attributeModifier(this.modifier)
          .onClick(() => {
            this.modifier.isDark = !this.modifier.isDark
          })
      }
      .width('100%')
    }
    .height('100%')
  }
}
javascript 复制代码
// setAttribute.ets
export class MyButtonModifier implements AttributeModifier<ButtonAttribute> {
  isDark: boolean = false
  applyNormalAttribute(instance: ButtonAttribute): void {
    if (this.isDark) {
      instance.backgroundColor(Color.Black)
    } else {
      instance.backgroundColor(Color.Red)
    }
  }
}

(4)定义在组件内的@Styles可以通过this访问组件的常量和状态变量,并可以在@Styles里通过事件来改变状态变量的值,示例如下:

javascript 复制代码
@Component
struct FancyUse {
  @State heightValue: number = 100
  @Styles fancy() {
    .height(this.heightValue)
    .backgroundColor(Color.Yellow)
    .onClick(() => {
      this.heightValue = 200
    })
  }
}

(5)组件内@Styles的优先级高于全局@Styles。

框架优先找当前组件内的@Styles,如果找不到,则会全局查找。

1.2.2. 使用场景

以下示例中演示了组件内@Styles和全局@Styles的用法。

javascript 复制代码
import { TitleBar } from '../../components/common/TitleBar'
import { router } from '@kit.ArkUI'
import { RouterParams } from 'zzslib'

/**
 * @Styles可以定义在组件内、外
 */
/**
 * 组件外
 * 在组件外定义时需带上function关键字
 */
@Styles
function styleGlobal() {
  .backgroundColor(Color.Red)
  .width(100)
  .height(100)
}

@Styles
function styleGlobal2() {
  // .backgroundColor(color)
  .width(100)
  .height(100)
}

@Entry
@Component
struct StylePage {
  @State pageTitle: string = "网格列表"

  aboutToAppear() {
    try {
      this.pageTitle = (router
        .getParams() as RouterParams).title
    } catch (e) {
    }
  }
  /**
   * 组件内
   * 在组件外定义时不需要带function关键字
   */
  @Styles
  styleFancy() {
    .backgroundColor(Color.Blue)
    .width(100)
    .height(100)
  }

  build() {
    Column() {
      TitleBar({ pageTitle: $pageTitle })
      Text("组件外样式")
        .styleGlobal()
        .fontSize(30)
      Text("组件内样式")
        .styleFancy()
        .fontSize(30)
      //@Styles还可以在StateStyles属性内部使用,
      // 在组件处于不同的状态时赋予相应的属性。
      //在StateStyles内可以直接调用组件外定义的Styles,
      // 但需要通过this关键字调用组件内定义的Styles。
      Button() {
        Text("StateStyles")
      }
      .stateStyles({
        normal: {
          .width(180)
          .height(180)
        },
        disabled: this.styleFancy,
        pressed: styleGlobal
      })
    }
  }
}

1.3. @Extend 扩展原生组件样式

@Extend,用于扩展原生组件样式
**  说明:**

从API version 9开始,该装饰器支持在ArkTS卡片中使用。

从API version 11开始,该装饰器支持在元服务中使用。

1.3.1. 装饰器使用说明

1.3.1.1. 语法
javascript 复制代码
@Extend(UIComponentName) function functionName { ... }
1.3.1.2. 使用规则

(1)和@Styles不同,@Extend仅支持在全局定义,不支持在组件内部定义。
**  说明**

只能在当前文件内使用,不支持export

如果想实现export功能,推荐使用AttributeModifier

(2)和@Styles不同,@Extend支持封装指定组件的私有属性、私有事件和自身定义的全局方法。

javascript 复制代码
// @Extend(Text)可以支持Text的私有属性fontColor
@Extend(Text) function fancy () {
  .fontColor(Color.Red)
}
// superFancyText可以调用预定义的fancy
@Extend(Text) function superFancyText(size:number) {
    .fontSize(size)
    .fancy()
}

(3)和@Styles不同,@Extend装饰的方法支持参数,开发者可以在调用时传递参数,调用遵循TS方法传值调用。

javascript 复制代码
// xxx.ets
@Extend(Text) function fancy (fontSize: number) {
  .fontColor(Color.Red)
  .fontSize(fontSize)
}

@Entry
@Component
struct FancyUse {
  build() {
    Row({ space: 10 }) {
      Text('Fancy')
        .fancy(16)
      Text('Fancy')
        .fancy(24)
    }
  }
}

(4)@Extend装饰的方法的参数可以为function,作为Event事件的句柄。

javascript 复制代码
@Extend(Text) function makeMeClick(onClick: () => void) {
  .backgroundColor(Color.Blue)
  .onClick(onClick)
}
@Entry
@Component
struct FancyUse {
  @State label: string = 'Hello World';
  onClickHandler() {
    this.label = 'Hello ArkUI';
  }
  build() {
    Row({ space: 10 }) {
      Text(`${this.label}`)
        .makeMeClick(() => {this.onClickHandler()})
    }
  }
}

(5)@Extend的参数可以为状态变量,当状态变量改变时,UI可以正常的被刷新渲染。

javascript 复制代码
@Extend(Text) function fancy (fontSize: number) {
  .fontColor(Color.Red)
  .fontSize(fontSize)
}

@Entry
@Component
struct FancyUse {
  @State fontSizeValue: number = 20
  build() {
    Row({ space: 10 }) {
      Text('Fancy')
        .fancy(this.fontSizeValue)
        .onClick(() => {
          this.fontSizeValue = 30
        })
    }
  }
}

1.3.2. 使用场景

通过@Extend组合样式后,使得代码更加简洁,增强可读性。

javascript 复制代码
import { router } from '@kit.ArkUI'
import { RouterParams } from 'zzslib'
import { TitleBar } from '../../components/common/TitleBar'

@Extend(Text)
function extendText() {
  .fontSize(15)
  .maxLines(1)
  .textOverflow({ overflow: TextOverflow.Ellipsis })
  .margin({
    top: 20,
    right: 20,
    bottom: 20,
    left: 20
  })
  .padding(20)
  .border({
    width: {
      left: 1,
      right: 1,
      top: 1,
      bottom: 1
    },
    color: {
      left: Color.Blue,
      right: Color.Blue,
      top: Color.Red,
      bottom: $r('app.color.primary_font_content')
    },
    radius: {
      topLeft: 0,
      topRight: 0,
      bottomLeft: 0,
      bottomRight: 0
    },
    style: {
      left: BorderStyle.Dotted,
      right: BorderStyle.Dotted,
      top: BorderStyle.Solid,
      bottom: BorderStyle.Solid
    }
  })
}

// @Extend(Text)可以支持Text的私有属性fontColor
@Extend(Text)
function fancy() {
  .fontColor(Color.Red)
}

// superFancyText可以调用预定义的fancy
@Extend(Text)
function superFancyText(size: number) {
  .fontSize(size)
  .fancy()
}

@Extend(Text)
function extendTextLine(fontSize: number) {
  .fontSize(fontSize)
  .fontColor($r('app.color.primary_font_title'))
  .maxLines(1)
  .margin({ top: 20 })
}

@Entry
@Component
struct ExtendPage {
  @State pageTitle: string = "Extend"

  aboutToAppear() {
    try {
      this.pageTitle = (router
        .getParams() as RouterParams).title
    } catch (e) {
    }
  }

  build() {
    Column() {
      TitleBar({ pageTitle: $pageTitle })
      Text('全局样式')
        .extendText()
      Text('私有事件和自身定义的全局方法')
        .superFancyText(20)
      Text('@Extend装饰传递参数')
        .extendTextLine(20)
    }
  }
}

1.4. AttributeModifier

声明式语法引入了@Styles和@Extend两个装饰器,可以解决部分复用的问题,但是存在以下受限场景,为此,ArkUI引入了AttributeModifier的机制,可以通过Modifier对象动态修改属性。AttributeModifier的能力和灵活性更好,当前持续在构建全量的属性、事件设置能力。未来,AttributeModifier可以替代@Styles和@Extend几乎所有的能力,建议上述场景都使用AttributeModifier。

1.4.1.接口定义

javascript 复制代码
declare interface AttributeModifier<T> {

  applyNormalAttribute?(instance: T): void;
  
  applyPressedAttribute?(instance: T): void;
  
  applyFocusedAttribute?(instance: T): void;
  
  applyDisabledAttribute?(instance: T): void;
  
  applySelectedAttribute?(instance: T): void;

}

AttributeModifier是一个接口,需要开发者实现ApplyXxxAttribute的方法。Xxx表示多态的场景,支持默认态、按压态、焦点态、禁用态、选择态。其中,T是组件的属性类型,开发者可以在回调中获取到属性对象,通过该对象设置属性。

javascript 复制代码
declare class CommonMethod<T> {
  attributeModifier(modifier: AttributeModifier<T>): T;
}
}

在组件的通用方法上,增加了attributeModifier传入自定义的Modifier。由于组件在实例化时会明确T的类型,所以调用该方法时,T必须是组件对应的Attribute类型,或者是CommonAttribute。

1.4.2. 行为规格

(1)组件通用方法attributeModifier支持传入一个实现AttributeModifier接口的实例,T必须是组件对应的Attribute类型,或者是CommonAttribute。

(2)在组件首次初始化或者关联的状态变量发生变化时,如果传入的实例实现了对应接口,会触发applyNormalAttribute。

(3)回调applyNormalAttribute时,会传入组件属性对象,通过该对象可以设置当前组件的属性/事件。

暂未支持的属性/事件,执行时会抛异常。

(4)属性变化触发ApplyXxxAttribute函数时,该组件之前已设置的属性,在本次变化后未设置的属性会恢复为属性的默认值。

(5)可以通过该接口使用多态样式的功能,例如如果需要在组件进入按压态时设置某些属性,就可以通过自定义实现applyPressedAttribute方法完成。

(6)一个组件上同时使用属性方法和applyNormalAttribute设置相同的属性,遵循属性覆盖原则,即后设置的属性生效。

(7)一个Modifier实例对象可以在多个组件上使用。

(8)一个组件上多次使用applyNormalAttribute设置不同的Modifier实例,每次状态变量刷新均会按顺序执行这些实例的方法属性设置,同样遵循属性覆盖原则。

1.4.3. 属性设置与修改

AttributeModifier可以分离UI与样式,支持参数传递及业务逻辑编写,并且通过状态变量触发刷新。

javascript 复制代码
// button_modifier.ets
export class MyButtonModifier implements AttributeModifier<ButtonAttribute> {
  // 可以实现一个Modifier,定义私有的成员变量,外部可动态修改
  isDark: boolean = false

  // 通过构造函数,创建时传参
  constructor(dark?: boolean) {
    this.isDark = dark ? dark : false
  }

  applyNormalAttribute(instance: ButtonAttribute): void {
    // instance为Button的属性对象,可以通过instance对象对属性进行修改
    if (this.isDark) { // 支持业务逻辑的编写
      // 属性变化触发apply函数时,变化前已设置并且变化后未设置的属性会恢复为默认值
      instance.backgroundColor(Color.Black)
    } else {
      // 支持属性的链式调用
      instance.backgroundColor(Color.Red)
        .borderColor(Color.Black)
        .borderWidth(2)
    }
  }
}
javascript 复制代码
// demo.ets
import { MyButtonModifier } from './button_modifier'

@Entry
@Component
struct attributeDemo {
  // 支持用状态装饰器修饰,行为和普通的对象一致
  @State modifier: MyButtonModifier = new MyButtonModifier(true);

  build() {
    Row() {
      Column() {
        Button("Button")
          .attributeModifier(this.modifier)
          .onClick(() => {
            // 对象的一层属性被修改时,会触发UI刷新,重新执行applyNormalAttribute
            this.modifier.isDark = !this.modifier.isDark
          })
      }
      .width('100%')
    }
    .height('100%')
  }
}

一个组件上同时使用属性方法和applyNormalAttribute设置相同的属性,遵循属性覆盖原则,即后设置的属性生效。

c 复制代码
// button_modifier.ets
export class MyButtonModifier implements AttributeModifier<ButtonAttribute> {
  isDark: boolean = false

  constructor(dark?: boolean) {
    this.isDark = dark ? dark : false
  }

  applyNormalAttribute(instance: ButtonAttribute): void {
    if (this.isDark) {
      instance.backgroundColor(Color.Black)
    } else {
      instance.backgroundColor(Color.Red)
        .borderColor(Color.Black)
        .borderWidth(2)
    }
  }
}
c 复制代码
// demo.ets
import { MyButtonModifier } from './button_modifier';

@Entry
@Component
struct attributeDemo {
  @State modifier: MyButtonModifier = new MyButtonModifier(true);

  build() {
    Row() {
      Column() {
        // 先设置属性,后设置modifier,按钮颜色会跟随modifier的值改变
        Button("Button")
          .backgroundColor(Color.Blue)
          .attributeModifier(this.modifier)
          .onClick(() => {
            this.modifier.isDark = !this.modifier.isDark
          })
      }
      .width('100%')
    }
    .height('100%')
  }
}

一个组件上多次使用applyNormalAttribute设置不同的Modifier实例,每次状态变量刷新均会按顺序执行这些实例的方法属性设置,遵循属性覆盖原则,即后设置的属性生效。

c 复制代码
// button_modifier.ets
export class MyButtonModifier implements AttributeModifier<ButtonAttribute> {
  isDark: boolean = false

  constructor(dark?: boolean) {
    this.isDark = dark ? dark : false
  }

  applyNormalAttribute(instance: ButtonAttribute): void {
    if (this.isDark) {
      instance.backgroundColor(Color.Black)
        .width(200)
    } else {
      instance.backgroundColor(Color.Red)
        .width(100)
    }
  }
}
c 复制代码
// button_modifier2.ets
export class MyButtonModifier2 implements AttributeModifier<ButtonAttribute> {
  isDark2: boolean = false

  constructor(dark?: boolean) {
    this.isDark2 = dark ? dark : false
  }

  applyNormalAttribute(instance: ButtonAttribute): void {
    if (this.isDark2) {
      instance.backgroundColor('#2787D9')
    } else {
      instance.backgroundColor('#707070')
    }
  }
}
c 复制代码
// demo.ets
import { MyButtonModifier } from './button_modifier';
import { MyButtonModifier2 } from './button_modifier2';

@Entry
@Component
struct attributeDemo {
  @State modifier: MyButtonModifier = new MyButtonModifier(true);
  @State modifier2: MyButtonModifier2 = new MyButtonModifier2(true);

  build() {
    Row() {
      Column() {
        Button("Button")
          .attributeModifier(this.modifier)
          .attributeModifier(this.modifier2)
          .onClick(() => {
            this.modifier.isDark = !this.modifier.isDark
            this.modifier2.isDark2 = !this.modifier2.isDark2
          })
      }
      .width('100%')
    }
    .height('100%')
  }
}

1.4.4. 设置多态样式、事件

使用AttributeModifier设置多态样式、事件,实现事件逻辑的复用,支持默认态、按压态、焦点态、禁用态、选择态。例如如果需要在组件进入按压态时设置某些属性,就可以通过自定义实现applyPressedAttribute方法完成。

c 复制代码
// button_modifier.ets
export class MyButtonModifier implements AttributeModifier<ButtonAttribute> {
  applyNormalAttribute(instance: ButtonAttribute): void {
    // instance为Button的属性对象,设置正常状态下属性值
    instance.backgroundColor(Color.Red)
      .borderColor(Color.Black)
      .borderWidth(2)
  }
  applyPressedAttribute(instance: ButtonAttribute): void {
    // instance为Button的属性对象,设置按压状态下属性值
    instance.backgroundColor(Color.Green)
      .borderColor(Color.Orange)
      .borderWidth(5)
  }
}
c 复制代码
// demo.ets
import { MyButtonModifier } from './button_modifier'

@Entry
@Component
struct attributeDemo {
  @State modifier: MyButtonModifier = new MyButtonModifier();

  build() {
    Row() {
      Column() {
        Button("Button")
          .attributeModifier(this.modifier)
      }
      .width('100%')
    }
    .height('100%')
  }
}

1.4.5. 示例

c 复制代码
import { CommonModifier, router } from '@kit.ArkUI'
import { RouterParams } from 'zzslib'
import { TitleBar } from '../../components/common/TitleBar'

class MyButtonModifier implements AttributeModifier<ButtonAttribute> {
  isDark: boolean = false

  // applyNormalAttribute(instance: ButtonAttribute): void {
  //   if (this.isDark) {
  //     instance.backgroundColor(Color.Black)
  //   } else {
  //     instance.backgroundColor(Color.Red)
  //   }
  // }

  /**
   * 组件普通状态时的样式
   * @param instance
   */
  applyNormalAttribute(instance: ButtonAttribute): void {
    instance.backgroundColor(Color.Black)
  }

  /**
   * 组件按压状态的样式
   * @param instance
   */
  applyPressedAttribute(instance: ButtonAttribute): void {
    instance.backgroundColor(Color.Red)
  }

  /**
   * 组件禁用状态的样式
   * @param instance
   */
  applyDisabledAttribute(instance: ButtonAttribute): void {

  }

  /**
   * 组件选中状态的样式
   * @param instance
   */
  applySelectedAttribute(instance: ButtonAttribute): void {
  }
}

class MyModifier extends CommonModifier {
  applyNormalAttribute(instance: CommonAttribute): void {
    super.applyNormalAttribute?.(instance);
  }

  public setGroup1(): void {
    this.borderStyle(BorderStyle.Dotted)
    this.borderWidth(8)
  }

  public setGroup2(): void {
    this.borderStyle(BorderStyle.Dashed)
    this.borderWidth(8)
  }
}

@Component
struct MyImage1 {
  @Link modifier: CommonModifier

  build() {
    Image($r("app.media.icon_main_apply_normal"))
      .attributeModifier(this.modifier as MyModifier)
  }
}

/**
 * 设置自定义Modifier给一个组件,该组件对应属性生效。
 * 自定义Modifier属性值变化,组件对应属性也会变化。自定义Modifier类型为基类,
 * 构造的对象为子类对象,使用时要通过as进行类型断言为子类。
 * 一个自定义Modifier设置给两个组件,Modifier属性变化的时候对两个组件同时生效。
 * 一个Modifier设置了属性A和属性B,再设置属性C和属性D,4个属性同时在组件上生效。
 * 自定义Modifier不支持@State标注的状态数据的变化感知,见示例2。
 * 多次通过attributeModifier设置属性时,生效的属性为所有属性的并集,相同属性按照设置顺序生效。
 */
@Entry
@Component
struct AttributeModifierPage {
  @State pageTitle: string = "自定义样式"
  @State modifier: MyButtonModifier = new MyButtonModifier()
  @State myModifier: CommonModifier = new MyModifier()
    .width(100).height(100).margin(10)
  index: number = 0;

  aboutToAppear() {
    try {
      this.pageTitle = (router.getParams() as RouterParams).title
    } catch (e) {
    }
  }

  build() {
    Column() {
      TitleBar({ pageTitle: $pageTitle })
      Column() {
        // Button("Button")
        //   .attributeModifier(this.modifier)
        //   .onClick(() => {
        //     this.modifier.isDark = !this.modifier.isDark
        //   })
        Button("Button")
          .attributeModifier(this.modifier)
        Button($r("app.string.EntryAbility_label"))
          .margin(10)
          .onClick(() => {
            console.log("Modifier", "onClick")
            this.index++;
            if (this.index % 2 === 1) {
              (this.myModifier as MyModifier).setGroup1()
              console.log("Modifier", "setGroup1")
            } else {
              (this.myModifier as MyModifier).setGroup2()
              console.log("Modifier", "setGroup2")
            }
          })
        MyImage1({ modifier: this.myModifier })
      }
      .width('100%')
    }
    .height('100%')
  }
}

1.5. AttributeUpdater

1.5.1. 概述

大量属性频繁更新时,如果使用状态变量,会导致前端状态管理计算量太大,并需要对单个组件进行全量的属性更新。虽然可以通过AttributeModifier的机制按需更新,但是前端还是默认会有一些diff和reset的策略。

为此引入了AttributeUpdater的能力,它是一个特殊的AttributeModifier,除了继承AttributeModifier的能力,还提供了获取属性对象的能力。通过属性对象可以不经过状态变量,直接更新对应属性。使用AttributeUpdater,开发者可实现自定义的更新策略,进一步提高属性更新的性能。但是由于该能力比较灵活,无法限制"单一数据源"的规则,同时和状态变量更新相同属性时,存在相互覆盖的情况,需要开发者自己保障属性设置的合理性。

1.5.2. 接口定义

c 复制代码
export declare class AttributeUpdater<T, C = Initializer<T>> implements AttributeModifier<T> {
  applyNormalAttribute?(instance: T): void;
  initializeModifier(instance: T): void;
  get attribute(): T | undefined;
  updateConstructorParams: C;
}

AttributeUpdater实现了AttributeModifier接口,额外提供了initializeModifier,可以对组件的属性进行初始化,并且通过attribute属性方法,获取到属性对象,通过该对象直接更新对应组件的属性。另外也可以直接通过updateConstructorParams更新组件的构造参数。

1.5.3. 行为规格

(1)开发者可以实现一个AttributeUpdater的类,并通过组件的AttributeModifier设置,首次绑定时会触发initializeModifier方法,进行属性的初始化,后续其它的生命周期和AttributeModifier保持一致。

(2)组件初始化完成之后,可以通过AttributeUpdater实例的attribute属性方法,获取到属性对象,否则为undefined。

(3)通过attribute属性对象直接修改属性,会将最新设置的属性记录在当前对象中,并立即触发组件属性的更新。

(4)如果将AttributeUpdater实例标记为状态变量进行修改,或者通过其它状态变量更新对应组件的属性,会触发applyNormalAttribute的流程,如果开发者没有覆写该逻辑,默认会将属性对象记录的所有属性,批量进行一次更新。

(5)如果开发者复写applyNormalAttribute的逻辑,并且不调用super的该方法,将会失去获取attribute属性对象的能力,不会调用initializeModifier方法。

(6)一个AttributeUpdater对象只能同时关联一个组件,否则只会有一个组件生效属性设置。

1.5.4. 通过modifier直接修改属性

组件初始化完成之后,可以通过AttributeUpdater实例的attribute属性方法,获取到属性对象,通过属性对象直接修改属性,会立即触发组件属性的更新。

c 复制代码
import { AttributeUpdater } from '@ohos.arkui.modifier'

class MyButtonModifier extends AttributeUpdater<ButtonAttribute> {
  initializeModifier(instance: ButtonAttribute): void {
    instance.backgroundColor('#2787D9')
      .width('50%')
      .height(30)
  }
}

@Entry
@Component
struct updaterDemo {
  modifier: MyButtonModifier = new MyButtonModifier()

  build() {
    Row() {
      Column() {
        Button("Button")
          .attributeModifier(this.modifier)
          .onClick(() => {
            this.modifier.attribute?.backgroundColor('#17A98D').width('30%')
          })
      }
      .width('100%')
    }
    .height('100%')
  }
}

1.5.4. 通过modifier更新组件的构造参数

可以直接通过AttributeUpdater实例的updateConstructorParams方法,更新组件的构造参数。

c 复制代码
import { AttributeUpdater } from '@ohos.arkui.modifier'

class MyTextModifier extends AttributeUpdater<TextAttribute, TextInterface> {
  initializeModifier(instance: TextAttribute): void {
  }
}

@Entry
@Component
struct updaterDemo {
  modifier: MyTextModifier = new MyTextModifier()

  build() {
    Row() {
      Column() {
        Text("Text")
          .attributeModifier(this.modifier)
          .fontColor(Color.White)
          .fontSize(14)
          .border({ width: 1 })
          .textAlign(TextAlign.Center)
          .lineHeight(20)
          .width(200)
          .height(50)
          .backgroundColor('#2787D9')
          .onClick(() => {
            this.modifier.updateConstructorParams('Update');
          })
      }
      .width('100%')
    }
    .height('100%')
  }
}
相关推荐
weixin_307779131 小时前
研究深度神经网络优化稳定性,证明在一定条件下梯度下降和随机梯度下降方法能有效控制损失函数
深度学习·机器学习·dnn
SuperHeroWu72 小时前
【HarmonyOS】判断应用是否已安装
华为·微信·harmonyos·qq·微博·应用是否安装·canopenlink
SoraLuna2 小时前
「Mac畅玩鸿蒙与硬件7」鸿蒙开发环境配置篇7 - 使用命令行工具和本地模拟器管理项目
macos·华为·harmonyos
山水无移11 小时前
RT-DETR-V2 TensorRT C++ 部署
人工智能·深度学习
没有不重的名么11 小时前
循环神经网络RNN
人工智能·rnn·深度学习
sp_fyf_202413 小时前
计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-10-27
人工智能·深度学习·算法·机器学习·语言模型·自然语言处理·数据挖掘
SuperHeroWu713 小时前
【HarmonyOS】鸿蒙应用OAID广告标识ID设置设备唯一标识
华为·harmonyos·oaid·广告标识·跟踪权限
sp_fyf_202414 小时前
计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-10-26
人工智能·深度学习·神经网络·算法·机器学习·语言模型·自然语言处理
佛山芃程科技16 小时前
鸿蒙NEXT+Flutter开发7-存储应用设置项
harmonyos