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

news2024/11/22 13:21:27

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

  @Styles装饰器:定义组件重用样式
  ;@Extend装饰器:定义扩展组件样式
  自定义扩展:AttributeModifier、AttributeUpdater

1.1. 区别

1.1.1. 对比

  声明式语法引入了@Styles和@Extend两个装饰器,可以解决部分复用的问题,但是存在以下受限场景:
  (1)@Styles和@Extend均是编译期处理,不支持跨文件的导出复用。
  (2)@Styles仅能支持通用属性、事件,不支持组件特有的属性。
  (3)@Styles虽然支持在多态样式下使用,但不支持传参,无法对外开放一些属性。
  (4)@Extend虽然能支持特定组件的私有属性、事件,但同样不支持跨文件导出复用。
  (5)@Styles、@Extend对于属性设置,无法支持业务逻辑编写,动态决定是否设置某些属性。只能通过三元表达式对所有可能设置的属性进行全量设置,设置大量属性时效率低下。
  (为了解决上述问题,ArkUI引入了AttributeModifier的机制,可以通过Modifier对象动态修改属性。能力对比如下:

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

  可以看出,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方法不支持参数,反例如下。

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

  (2)@Styles可以定义在组件内或全局,在全局定义时需在方法名前面添加function关键字,组件内定义时则不需要添加function关键字。
  说明
  只能在当前文件内使用,不支持export。
  如果想实现export功能,推荐使用AttributeModifier

// 全局
@Styles function functionName() { ... }

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

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

// 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%')
  }
}
// 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里通过事件来改变状态变量的值,示例如下:

@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的用法。

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. 语法
@Extend(UIComponentName) function functionName { ... }
1.3.1.2. 使用规则

  (1)和@Styles不同,@Extend仅支持在全局定义,不支持在组件内部定义。
  说明
  只能在当前文件内使用,不支持export
  如果想实现export功能,推荐使用AttributeModifier
  (2)和@Styles不同,@Extend支持封装指定组件的私有属性、私有事件和自身定义的全局方法。

// @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方法传值调用。

// 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事件的句柄。

@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可以正常的被刷新渲染。

@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组合样式后,使得代码更加简洁,增强可读性。

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.接口定义

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是组件的属性类型,开发者可以在回调中获取到属性对象,通过该对象设置属性。

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与样式,支持参数传递及业务逻辑编写,并且通过状态变量触发刷新。

// 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)
    }
  }
}
// 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设置相同的属性,遵循属性覆盖原则,即后设置的属性生效。

// 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)
    }
  }
}
// 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实例,每次状态变量刷新均会按顺序执行这些实例的方法属性设置,遵循属性覆盖原则,即后设置的属性生效。

// 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)
    }
  }
}
// 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')
    }
  }
}
// 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方法完成。

// 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)
  }
}
// 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. 示例

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. 接口定义

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属性方法,获取到属性对象,通过属性对象直接修改属性,会立即触发组件属性的更新。

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方法,更新组件的构造参数。

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%')
  }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2224561.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

HarmonyOS 5.0应用开发——应用打包HAP、HAR、HSP

【高心星出品】 目录 应用打包HAP、HAR、HSPModule类型HAPHAR创建HAR建立依赖HAR共享内容 HSP创建HSP建立依赖同上HSP共享内容同上 HAR VS HSP 应用打包HAP、HAR、HSP 一个应用通常会包含多种功能&#xff0c;将不同的功能特性按模块来划分和管理是一种良好的设计方式。在开发…

【哈工大_操作系统实验】Lab9 proc文件系统的实现

本节将更新哈工大《操作系统》课程第九个 Lab 实验 proc文件系统的实现。按照实验书要求&#xff0c;介绍了非常详细的实验操作流程&#xff0c;并提供了超级无敌详细的代码注释。 实验目的&#xff1a; 掌握虚拟文件系统的实现原理&#xff1b;实践文件、目录、文件系统等概念…

【C++开篇】

首先初阶的数据结构相信大家已经学习的差不多了&#xff0c;关于初阶数据结构排序的相关内容的总结随后我也会给大家分享出来。C语言和C有许多相同的地方&#xff0c;但也有许多不相同的地方。接下来的C部分&#xff0c;我们主要是针对C与C语言不同的地方来与大家进行分享。其中…

量子变分算法 (python qiskit)

背景 变分量子算法是用于观察嘈杂的近期设备上的量子计算效用的有前途的候选混合算法。变分算法的特点是使用经典优化算法迭代更新参数化试验解决方案或“拟设”。这些方法中最重要的是变分量子特征求解器 (VQE)&#xff0c;它旨在求解给定汉密尔顿量的基态&#xff0c;该汉密尔…

这是一篇vue3 的详细教程

Vue 3 详细教程 一、Vue 3 简介 Vue.js 是一款流行的 JavaScript 前端框架&#xff0c;用于构建用户界面。Vue 3 是其最新版本&#xff0c;带来了许多新特性和性能优化&#xff0c;使开发更加高效和灵活。 二、环境搭建 安装 Node.js 前往Node.js 官方网站下载并安装适合你…

WPF+MVVM案例实战(六)- 自定义分页控件实现

文章目录 1、项目准备2、功能实现1、分页控件 DataPager 实现2、分页控件数据模型与查询行为3、数据界面实现 3、运行效果4、源代码获取 1、项目准备 打开项目 Wpf_Examples&#xff0c;新建 PageBarWindow.xaml 界面、PageBarViewModel.cs ,在用户控件库 UserControlLib中创建…

WASM 使用说明23事(RUST实现)

文章目录 1. wasm是什么1.1 chatgpt定义如下:1.2 wasm关键特性&#xff1a; 2. wasm demo2.1 cargo 创建项目2.2 编写code2.3 安装wasm-pack2.4 编译 3.1 html页面引用wasm代码&#xff08;js引用&#xff09;3.2 访问页面4 导入js function4.1 编写lib.rs文件&#xff0c;内容…

UML 总结(基于《标准建模语言UML教程》)

定义 UML 又称为统一建模语言或标准建模语言&#xff0c;是一种标准的图形化建模语言&#xff0c;它是面向对象分析与设计的一种标准表示。尽管UML 本身没有对过程有任何定义&#xff0c;但UML 对任何使用它的方法&#xff08;或过程&#xff09;提出的要求是&#xff1a;支持用…

【含开题报告+文档+PPT+源码】基于vue框架的东升餐饮点餐管理平台的设计与实现

开题报告 在当前信息化社会背景下&#xff0c;餐饮行业正经历着由传统线下服务模式向线上线下深度融合的转变。随着移动互联网技术及大数据应用的飞速发展&#xff0c;用户对于餐饮服务平台的需求也日益多元化和个性化。他们期望能在一个集便捷、高效、个性化于一体的平台上完…

自动化测试工具Ranorex Studio(十六)-添加新Action

在Action表中&#xff0c;有两种手动添加action的方式。 一种方法是指定Action本身&#xff08;’添加新Action’&#xff09;&#xff0c;然后分配对应的对象库条目&#xff08;在多数情况下&#xff09;。 第二种方法是直接把对象库条目拖放到Action表内&#xff0c;然后生成…

力扣 中等 2466.统计构造好字符串的方案数

文章目录 题目介绍题解 题目介绍 题解 题意&#xff1a;每次可以爬 zero 或 one 个台阶&#xff0c;返回爬 low 到 high 个台阶的方案数。 和上题337.组合总和 &#xff08;链接&#xff09;的思路一样&#xff0c;只不过是将可以爬的台阶数从数组换成了两个数&#xff08;ze…

视频美颜平台的搭建指南:基于直播美颜SDK的完整解决方案

众所周知&#xff0c;直播美颜SDK是实现视频美颜功能的核心。本文将详细解析如何基于直播美颜SDK搭建一个完整的视频美颜平台。 一、视频美颜SDK的核心功能 直播美颜SDK作为平台的技术核心&#xff0c;能够提供丰富的美颜效果和稳定的视频处理能力。通常&#xff0c;SDK具备以…

iOS AVAudioSession 详解【音乐播放器的配置】

前言 在 iOS 音频开发中&#xff0c;AVAudioSession 是至关重要的工具&#xff0c;它控制着应用的音频行为&#xff0c;包括播放、录音、后台支持和音频中断处理等。对于音乐播放器等音频需求强烈的应用&#xff0c;设计一个合理的 AVAudioSession 管理体系不仅能保证音频播放…

RabbitMQ是一个开源的消息代理和队列服务器

RabbitMQ是一个开源的消息代理和队列服务器&#xff0c;它基于AMQP&#xff08;Advanced Message Queuing Protocol&#xff0c;高级消息队列协议&#xff09;协议实现&#xff0c;同时也支持其他消息协议如STOMP、MQTT等。作为一个可靠的消息传递服务&#xff0c;RabbitMQ在分…

青少年CTF平台的基础题writeup

青少年CTF平台 1、文章管理系统 首先他这里有一个问号id&#xff0c;就想着使用mysql跑一下&#xff0c;但是windows的sqlmap很不给力&#xff0c;都不动&#xff0c;所以我后面换成了kali机来跑 跑一下就跑出了好多的数据库 第一次我使用ctftraining不出货&#xff0c;跑的一…

Coppelia Sim (v-REP)仿真 机器人3D相机手眼标定与实时视觉追踪 (一)

coppelia sim[V-REP]仿真实现 机器人于3D相机手眼标定与实时视觉追踪 一 标定板的制作生成标定的PDF文件PDF转为图像格式图像加载到仿真中 二 仿真场景设置加载机器人加载的控制dummy ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/b48549d355d8441d8dfc20bc7ba7196…

【K8S系列】Kubernetes Pod节点CrashLoopBackOff 状态及解决方案详解【已解决】

在 Kubernetes 中&#xff0c;Pod 的状态为 CrashLoopBackOff 表示某个容器在启动后崩溃&#xff0c;Kubernetes 尝试重启该容器&#xff0c;但由于持续崩溃&#xff0c;重启的间隔时间逐渐增加。下面将详细介绍 CrashLoopBackOff 状态的原因、解决方案及相关命令的输出解释。 …

.NET Core WebApi第1讲(概念):Web基础、AJAX、JQuery

动态页面&#xff1a;数据流动 / Web服务器 / Ajax / 前后端分离 / restful风格源栈课堂一起帮https://17bang.ren/Code/261 一、Web基础 二、AJAX诞生 三、JQuery

ctfshow(262,264)--反序列化漏洞--字符串逃逸

Web262 源代码&#xff1a; index.php: error_reporting(0); class message{public $from;public $msg;public $to;public $tokenuser;public function __construct($f,$m,$t){$this->from $f;$this->msg $m;$this->to $t;} }$f $_GET[f]; $m $_GET[m]; $t $_…

虚拟现实新纪元:VR/AR技术将如何改变娱乐与教育

内容概要 在当今科技飞速发展的时代&#xff0c;虚拟现实&#xff08;VR&#xff09;和增强现实&#xff08;AR&#xff09;技术不仅让我们的娱乐体验如虎添翼&#xff0c;更为教育变革注入了新活力。这些技术的飞跃进展&#xff0c;将原本平淡无奇的场景转变为令人沉醉的沉浸…