概述
声明式语法引入了@Styles和@Extend两个装饰器,可以解决部分复用的问题,但是存在以下受限场景:
- @Styles和@Extend均是编译期处理,不支持跨文件的导出复用。
- @Styles仅能支持通用属性、事件,不支持组件特有的属性。
- @Styles虽然支持在多态样式下使用,但不支持传参,无法对外开放一些属性。
- @Extend虽然能支持特定组件的私有属性、事件,但同样不支持跨文件导出复用。
- @Styles、@Extend对于属性设置,无法支持业务逻辑编写,动态决定是否设置某些属性。只能通过三元表达式对所有可能设置的属性进行全量设置,设置大量属性时效率低下。
为了解决上述问题,ArkUI引入了AttributeModifier的机制,可以通过Modifier对象动态修改属性。能力对比如下:
能力 | @Styles | @Extend | AttributeModifier |
---|---|---|---|
跨文件导出 | 不支持 | 不支持 | 支持 |
通用属性设置 | 支持 | 支持 | 支持 |
通用事件设置 | 支持 | 支持 | 部分支持 |
组件特有属性设置 | 不支持 | 支持 | 部分支持 |
组件特有事件设置 | 不支持 | 支持 | 部分支持 |
参数传递 | 不支持 | 支持 | 支持 |
多态样式 | 支持 | 不支持 | 支持 |
业务逻辑 | 不支持 | 不支持 | 支持 |
可以看出,AttributeModifier的能力和灵活性更好,当前持续在构建全量的属性、事件设置能力。未来,AttributeModifier可以替代@Styles和@Extend几乎所有的能力,建议上述场景都使用AttributeModifier。
接口定义
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。
行为规格
- 组件通用方法attributeModifier支持传入一个实现AttributeModifier<T>接口的实例,T必须是组件对应的Attribute类型,或者是CommonAttribute。
- 在组件首次初始化或者关联的状态变量发生变化时,如果传入的实例实现了对应接口,会触发applyNormalAttribute。
- 回调applyNormalAttribute时,会传入组件属性对象,通过该对象可以设置当前组件的属性/事件。
- 暂未支持的属性/事件,执行时会抛异常。
- 属性变化触发ApplyXxxAttribute函数时,该组件之前已设置的属性,在本次变化后未设置的属性会恢复为属性的默认值。
- 可以通过该接口使用多态样式的功能,例如如果需要在组件进入按压态时设置某些属性,就可以通过自定义实现applyPressedAttribute方法完成。
- 一个组件上同时使用属性方法和applyNormalAttribute设置相同的属性,遵循属性覆盖原则,即后设置的属性生效。
- 一个Modifier实例对象可以在多个组件上使用。
- 一个组件上多次使用applyNormalAttribute设置不同的Modifier实例,每次状态变量刷新均会按顺序执行这些实例的方法属性设置,同样遵循属性覆盖原则。
属性设置与修改
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%')
}
}
设置多态样式、事件
使用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%')
}
}
最后
有很多小伙伴不知道学习哪些鸿蒙开发技术?不知道需要重点掌握哪些鸿蒙应用开发知识点?而且学习时频繁踩坑,最终浪费大量时间。所以有一份实用的鸿蒙(HarmonyOS NEXT)资料用来跟着学习是非常有必要的。
点击→【纯血版鸿蒙全套最新学习资料】希望这一份鸿蒙学习资料能够给大家带来帮助!~
鸿蒙(HarmonyOS NEXT)最新学习路线
该路线图包含基础技能、就业必备技能、多媒体技术、六大电商APP、进阶高级技能、实战就业级设备开发,不仅补充了华为官网未涉及的解决方案
路线图适合人群:
IT开发人员:想要拓展职业边界
零基础小白:鸿蒙爱好者,希望从0到1学习,增加一项技能。
技术提升/进阶跳槽:发展瓶颈期,提升职场竞争力,快速掌握鸿蒙技术
2.视频学习资料+学习PDF文档
这份鸿蒙(HarmonyOS NEXT)资料包含了鸿蒙开发必掌握的核心知识要点,内容包含了(ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、音频、视频、WebGL、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、(南向驱动、嵌入式等)鸿蒙项目实战等等)鸿蒙(HarmonyOS NEXT)技术知识点。
HarmonyOS Next 最新全套视频教程
大厂面试必问面试题
鸿蒙南向开发技术
鸿蒙APP开发必备
《鸿蒙 (OpenHarmony)开发基础到实战手册》
OpenHarmony北向、南向开发环境搭建
《鸿蒙开发基础》
《鸿蒙开发进阶》
《鸿蒙进阶实战》
总结
总的来说,华为鸿蒙不再兼容安卓,对程序员来说是一个挑战,也是一个机会。只有积极应对变化,不断学习和提升自己,才能在这个变革的时代中立于不败之地。