鸿蒙中 属性修改器(AttributeModifier)-样式复用
本文介绍了HarmonyOS中AttributeModifier装饰器的核心功能和使用方法。与@Styles和@Extend相比,AttributeModifier支持跨文件复用、参数传递、业务逻辑和多态样式,解决了现有样式复用方案的局限性。文章详细阐述了接口定义、基础使用、属性覆盖原则、多Modifier组合、多态样式设置等核心功能,并提供了最佳实践建议。通过状态变量触发UI刷新,Attribu
本文同步发表于我的微信公众号,微信搜索 程语新视界 即可关注,每个工作日都有文章更新
在ArkUI开发中,经常需要复用样式代码。HarmonyOS提供了@Styles和@Extend装饰器来解决样式复用问题,但它们存在一些局限性:
| 能力对比 | @Styles | @Extend | AttributeModifier |
|---|---|---|---|
| 跨文件导出 | 不支持 | 不支持 | 支持 |
| 通用属性设置 | 支持 | 支持 | 支持 |
| 通用事件设置 | 支持 | 支持 | 部分支持 |
| 组件特有属性设置 | 不支持 | 支持 | 部分支持 |
| 组件特有事件设置 | 不支持 | 支持 | 部分支持 |
| 参数传递 | 不支持 | 支持 | 支持 |
| 多态样式 | 支持 | 不支持 | 支持 |
| 业务逻辑 | 不支持 | 不支持 | 支持 |
核心痛点:
-
@Styles和@Extend都是编译期处理,无法跨文件导出复用
-
@Styles只能支持通用属性/事件,不支持组件特有属性
-
@Styles虽支持多态样式,但不支持传参
-
无法编写业务逻辑动态决定属性设置,只能通过三元表达式全量设置
AttributeModifier的优势:
-
支持跨文件导出复用
-
支持参数传递
-
支持业务逻辑编写
-
支持多态样式(按压态、焦点态等)
-
可通过状态变量触发UI刷新
二、接口定义
2.1 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; // 选择态
}
接口说明:
-
T:组件的属性类型(如ButtonAttribute、CommonAttribute等) -
所有方法均为可选实现,根据需要选择实现对应场景
-
declare class CommonMethod<T> { attributeModifier(modifier: AttributeModifier<T>): T; }方法回调时会传入组件属性对象
instance,通过该对象设置属性
2.2 组件通用方法
使用方式:
-
组件实例化时调用
attributeModifier方法 -
传入自定义的Modifier实例
-
T必须指定为组件对应的Attribute类型或CommonAttribute
三、使用示例
3.1 基础使用:设置和修改组件属性
实现自定义Modifier
// Common/ButtonModifier01.ets
export class MyButtonModifier implements AttributeModifier<ButtonAttribute> {
// 定义私有成员变量,支持外部动态修改
public isDark: boolean = false
// 构造函数支持传参
constructor(dark?: boolean) {
this.isDark = dark ?? false
}
applyNormalAttribute(instance: ButtonAttribute): void {
// 支持业务逻辑编写
if (this.isDark) {
// 注意:变化前已设置且变化后未设置的属性会恢复为默认值
instance.backgroundColor('#707070')
} else {
// 支持属性的链式调用
instance.backgroundColor('#17A98D')
.borderColor('#707070')
.borderWidth(2)
}
}
}
在页面中使用
// pages/Button1.ets
import { MyButtonModifier } from '../Common/ButtonModifier01'
@Entry
@Component
struct Button1 {
// 支持用状态装饰器修饰
@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%')
}
}
运行效果:
-
初始状态:深色模式(isDark=true),按钮颜色为#707070
-
点击按钮:切换isDark值,按钮颜色在#17A98D和#707070之间切换
3.2 属性覆盖原则
当一个组件上同时使用属性方法和applyNormalAttribute设置相同的属性时,遵循后设置的属性生效原则。
// pages/Button2.ets
@Entry
@Component
struct Button2 {
@State modifier: MyButtonModifier = new MyButtonModifier(true);
build() {
Row() {
Column() {
// 先设置属性,后设置modifier
// 最终颜色会跟随modifier的值改变
Button('Button')
.backgroundColor('#2787D9') // 先设置蓝色
.attributeModifier(this.modifier) // 后设置modifier覆盖
.onClick(() => {
this.modifier.isDark = !this.modifier.isDark
})
}
.width('100%')
}
.height('100%')
}
}
运行逻辑:
-
初始时,Button先设置backgroundColor为蓝色
-
随后attributeModifier设置modifier(深色模式为#707070)
-
由于modifier后设置,最终显示#707070
-
点击切换时,modifier的isDark变化,触发UI刷新重新应用属性
3.3 多个Modifier组合使用
一个组件上可以多次使用attributeModifier设置不同的Modifier实例,按顺序执行,同样遵循后设置的属性生效原则。
// Common/ButtonModifier02.ets
export class MyButtonModifier2 implements AttributeModifier<ButtonAttribute> {
public isDark: boolean = false
constructor(dark?: boolean) {
this.isDark = dark ?? false
}
applyNormalAttribute(instance: ButtonAttribute): void {
if (this.isDark) {
instance.backgroundColor(Color.Black).width(200)
} else {
instance.backgroundColor(Color.Red).width(100)
}
}
}
// Common/ButtonModifier03.ets
export class MyButtonModifier3 implements AttributeModifier<ButtonAttribute> {
public isDark2: boolean = false
constructor(dark?: boolean) {
this.isDark2 = dark ?? false
}
applyNormalAttribute(instance: ButtonAttribute): void {
if (this.isDark2) {
instance.backgroundColor('#2787D9')
} else {
instance.backgroundColor('#707070')
}
}
}
// pages/Button3.ets
@Entry
@Component
struct Button3 {
@State modifier: MyButtonModifier2 = new MyButtonModifier2(true);
@State modifier2: MyButtonModifier3 = new MyButtonModifier3(true);
build() {
Row() {
Column() {
Button('Button')
.attributeModifier(this.modifier) // 先执行
.attributeModifier(this.modifier2) // 后执行,覆盖前面的backgroundColor设置
.onClick(() => {
this.modifier.isDark = !this.modifier.isDark
this.modifier2.isDark2 = !this.modifier2.isDark2
})
}
.width('100%')
}
.height('100%')
}
}
执行顺序:
-
先执行modifier,设置backgroundColor和width
-
再执行modifier2,重新设置backgroundColor(覆盖modifier的设置)
-
width仍保留modifier的设置(modifier2未设置width)
-
最终效果:backgroundColor由modifier2决定,width由modifier决定
3.4 多态样式与事件设置
AttributeModifier支持完整的多态样式,包括:
-
默认态(Normal)
-
按压态(Pressed)
-
焦点态(Focused)
-
禁用态(Disabled)
-
选择态(Selected)
// Common/ButtonModifier04.ets
export class MyButtonModifier4 implements AttributeModifier<ButtonAttribute> {
applyNormalAttribute(instance: ButtonAttribute): void {
// 正常状态下的样式
instance.backgroundColor('#17A98D')
.borderColor('#707070')
.borderWidth(2)
}
applyPressedAttribute(instance: ButtonAttribute): void {
// 按压状态下的样式
instance.backgroundColor('#2787D9')
.borderColor('#FFC000')
.borderWidth(5)
}
}
// pages/Button4.ets
@Entry
@Component
struct Button4 {
@State modifier: MyButtonModifier4 = new MyButtonModifier4();
build() {
Row() {
Column() {
Button('Button')
.attributeModifier(this.modifier)
}
.width('100%')
}
.height('100%')
}
}
交互效果:
-
正常显示:绿色背景(#17A98D),灰色边框(#707070),边框宽度2
-
按下时:蓝色背景(#2787D9),黄色边框(#FFC000),边框宽度5
-
松开后:恢复正常样式
四、使用原则
4.1 触发时机
-
组件首次初始化时触发
applyNormalAttribute -
关联的状态变量发生变化时触发对应方法
-
组件进入不同状态(按压、焦点等)时触发对应的
applyXxxAttribute方法
4.2 属性恢复规则
属性变化触发applyXxxAttribute函数时,该组件之前已设置的属性,在本次变化后未设置的属性会恢复为属性的默认值
applyNormalAttribute(instance: ButtonAttribute): void {
if (this.isDark) {
// 只设置了backgroundColor,borderColor和borderWidth会恢复默认值
instance.backgroundColor('#707070')
} else {
// 同时设置了三个属性
instance.backgroundColor('#17A98D')
.borderColor('#707070')
.borderWidth(2)
}
}
4.3 实例复用
一个Modifier实例对象可以在多个组件上使用:
@State modifier: MyButtonModifier = new MyButtonModifier(true);
build() {
Column() {
Button('按钮1').attributeModifier(this.modifier)
Button('按钮2').attributeModifier(this.modifier)
Button('按钮3').attributeModifier(this.modifier)
}
}
4.4 异常处理
对于暂未支持的属性/事件,执行时会抛异常。
五、属性/事件支持情况详表
5.1 不支持attributeModifier的属性/事件
以下属性/事件当前不支持通过attributeModifier设置:
| 组件/通用信息 | 属性/事件名称 | 说明 |
|---|---|---|
| CommonAttribute | accessibilityText | - |
| CommonAttribute | accessibilityDescription | - |
| CommonAttribute | animation | 不支持animation相关属性 |
| CommonAttribute | attributeModifier | 不支持嵌套使用,不生效 |
| CommonAttribute | backgroundFilter | - |
| CommonAttribute | chainWeight | - |
被注:完整列表请参考官方文档
5.2 起始版本与支持版本不一致的属性
部分属性的起始版本与支持attributeModifier的版本不一致:
| 组件 | 属性/事件 | 起始版本 | 支持Modifier版本 |
|---|---|---|---|
| Button | buttonStyle | 11 | 12 |
| Button | controlSize | 11 | 12 |
| CommonAttribute | onDragStart | 8 | 13 |
| CommonAttribute | onVisibleAreaChange | 9 | 20 |
| CommonAttribute | foregroundBlurStyle | 10 | 18 |
备注:完整列表请参考官方文档
六、其他
6.1 什么时候使用AttributeModifier?
-
需要跨文件复用样式代码
-
需要根据业务逻辑动态设置属性
-
需要支持参数传递的样式复用
-
需要实现多态样式(按压态、焦点态等)
-
需要将UI样式与业务逻辑分离
6.2 优化建议
-
合理拆分Modifier:将不同功能的属性拆分到不同的Modifier中,便于维护和复用
-
避免不必要的状态变量:只有需要动态变化的属性才使用状态变量
-
注意属性恢复规则:在apply方法中设置所有需要的属性,避免意外恢复默认值
6.3 设计模式推荐
// 推荐:使用Builder模式创建Modifier
export class ButtonModifierBuilder {
private modifier: MyButtonModifier;
constructor() {
this.modifier = new MyButtonModifier();
}
setDarkMode(isDark: boolean): ButtonModifierBuilder {
this.modifier.isDark = isDark;
return this;
}
build(): MyButtonModifier {
return this.modifier;
}
}
// 使用
const modifier = new ButtonModifierBuilder()
.setDarkMode(true)
.build();
更多推荐



所有评论(0)