跳到主要内容

属性修改器 (AttributeModifier)

概述

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

  • @Styles和@Extend均是编译期处理,不支持跨文件的导出复用。
  • @Styles仅能支持通用属性、事件,不支持组件特有的属性。
  • @Styles虽然支持在多态样式下使用,但不支持传参,无法对外开放一些属性。
  • @Extend虽然能支持特定组件的私有属性、事件,但同样不支持跨文件导出复用。
  • @Styles、@Extend对于属性设置,无法支持业务逻辑编写,动态决定是否设置某些属性,只能通过三元表达式对所有可能设置的属性进行全量设置,设置大量属性时效率较低。

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

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

可以看出,与@Styles和@Extend相比,AttributeModifier提供了更强的能力和灵活性,且在持续完善全量的属性和事件设置能力,因此推荐优先使用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表示多态的场景,支持默认态(Normal)、按压态(Pressed)、焦点态(Focused)、禁用态(Disabled)、选择态(Selected)。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与样式,支持参数传递及业务逻辑编写,并且通过状态变量触发刷新。

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

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

applyNormalAttribute(instance: ButtonAttribute): void {
// instance为Button的属性对象,可以通过instance对象对属性进行修改
if (this.isDark) { // 支持业务逻辑的编写
// 属性变化触发apply函数时,变化前已设置并且变化后未设置的属性会恢复为默认值
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%')
}
}

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

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

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

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

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

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

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

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)
}
}
}
export class MyButtonModifier3 implements AttributeModifier<ButtonAttribute> {
public 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')
}
}
}
// pages/Button3.ets
import { MyButtonModifier2 } from '../Common/ButtonModifier02';
import { MyButtonModifier3 } from '../Common/ButtonModifier03';

@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)
.onClick(() => {
this.modifier.isDark = !this.modifier.isDark
this.modifier2.isDark2 = !this.modifier2.isDark2
})
}
.width('100%')
}
.height('100%')
}
}

设置多态样式、事件

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

export class MyButtonModifier4 implements AttributeModifier<ButtonAttribute> {
applyNormalAttribute(instance: ButtonAttribute): void {
// instance为Button的属性对象,设置正常状态下属性值
instance.backgroundColor('#17A98D')
.borderColor('#707070')
.borderWidth(2)
}

applyPressedAttribute(instance: ButtonAttribute): void {
// instance为Button的属性对象,设置按压状态下属性值
instance.backgroundColor('#2787D9')
.borderColor('#FFC000')
.borderWidth(5)
}
}
// pages/Button4.ets
import { MyButtonModifier4 } from '../Common/ButtonModifier04'

@Entry
@Component
struct Button4 {
@State modifier: MyButtonModifier4 = new MyButtonModifier4();

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

属性或事件对attributeModifier的支持情况

通过attributeModifier动态设置属性或事件的能力从API version 11开始支持。

属性或事件不支持attributeModifier的范围

下表说明了当前不支持attributeModifier的属性或事件。若无特殊说明,属性或事件默认在首次开放时支持attributeModifier。

组件通用信息/系统组件的名称属性/事件的名称告警信息说明
CommonAttributeaccessibilityText--
CommonAttributeaccessibilityDescription--
CommonAttributeanimationMethod not implemented.不支持animation相关属性。
CommonAttributeattributeModifier-attributeModifier不支持嵌套使用,不生效。
CommonAttributebackgroundFilteris not callable-
CommonAttributechainWeightis not callable-
CommonAttributecompositingFilteris not callable-
CommonAttributedrawModifieris not callable不支持modifier相关的属性。
CommonAttributeforegroundFilteris not callable-
CommonAttributefreezeis not callable-
CommonAttributegestureMethod not implemented.不支持gesture相关的属性。
CommonAttributegestureModifieris not callable不支持modifier相关的属性。
CommonAttributeonAccessibilityHoveris not callable-
CommonAttributeonDigitalCrownis not callable.-
CommonAttributeparallelGestureMethod not implemented.不支持gesture相关的属性。
CommonAttributepriorityGestureMethod not implemented.不支持gesture相关的属性。
CommonAttributereuseIdMethod not implemented.-
CommonAttributestateStylesMethod not implemented.不支持stateStyles相关的属性。
CommonAttributeuseSizeTypeMethod not implemented.不支持已废弃属性。
CommonAttributevisualEffectis not callable-
CommonAttributebindContextMenuMethod not implemented.不支持入参为CustomBuilder。
CommonAttributebindContentCoverMethod not implemented.不支持入参为CustomBuilder。
CommonAttributebindSheetMethod not implemented.不支持入参为CustomBuilder。
CommonAttributedragPreviewBuilder is not supported.不支持入参为CustomBuilder。
CommonAttributebindPopupMethod not implemented.不支持入参为CustomBuilder。
CommonAttributeaccessibilityVirtualNodeis not callable不支持入参为CustomBuilder。
CommonAttributechainWeight--
CheckboxGroupcontentModifier--
CommonAttributebackgroundImage--
CommonAttributeonClick--
CommonAttributetoolbar--
CommonAttributeaccessibilityGroup--
CommonAttributereuse--
CommonAttributeonGestureRecognizerJudgeBegin--
EmbeddedComponentonError--
EmbeddedComponentonTerminated--
NavDestinationbackButtonIcon19+--
NavDestinationmenus19+--
NavDestinationcustomTransition--
NavigationbackButtonIcon--
Navigationmenus--
Repeateach--
Repeatkey--
RepeatvirtualScroll--
Repeattemplate--
RepeattemplateId--
SearchcustomKeyboard--
SearchonWillAttachIME--
SelectmenuItemContentModifier12+--
SelectmenuItemContentModifier18+--
SelecttextModifier--
SelectarrowModifier--
SelectoptionTextModifier--
SelectselectedOptionTextModifier--
SliderdigitalCrownSensitivity--
SwiperprevMargin--
SwipernextMargin--
TextAreacustomKeyboard--
TextbindSelectionMenu--
TextInputcustomKeyboard--
TextInputonWillAttachIME--
TextPickeronEnterSelectedArea--
TimePickeronEnterSelectedArea--

属性或事件的起始版本与支持attributeModifier版本不一致的范围

下表说明了属性或事件的起始版本与默认支持attributeModifier版本不一致的情况。若无特殊说明,属性或事件默认在首次开放时支持attributeModifier。

组件通用信息/系统组件的名称属性/事件的名称属性/事件的起始版本支持attributeModifier的版本
AlphabetIndexerautoCollapse1112
ButtonbuttonStyle1112
ButtoncontrolSize1112
CalendarPickeronChange1820
CanvasenableAnalyzer1220
CommonAttributeaccessibilityTextHint1220
CommonAttributeaccessibilityChecked1320
CommonAttributeaccessibilitySelected1320
CommonAttributebackground1020
CommonAttributevisualEffect1220
CommonAttributeonDragStart813
CommonAttributeonVisibleAreaApproximateChange1723
CommonAttributeonVisibleAreaChange920
CommonAttributeonTouchIntercept1220
CommonAttributeonPreDrag1220
CommonAttributeonChildTouchTest1120
CommonAttributebackgroundFilter1220
CommonAttributeforegroundFilter1220
CommonAttributecompositingFilter1220
CommonAttributeforegroundBlurStyle1018
CommonAttributefreeze12+1220
CommonAttributefreeze18+1820
CommonAttributedragPreviewOptions1112
CommonAttributebindMenu1120
CommonAttributetransition1220
CommonAttributesafeAreaPadding1418
CommonAttributepixelRound1112
ContainerSpantextBackgroundStyle1112
DatePickeronDateChange1820
FolderStackalignContent1112
FolderStackonFolderStateChange1120
FolderStackonHoverStatusChange1120
FolderStackenableAnimation1112
FolderStackautoHalfFold1112
GaugeprivacySensitive1220
ImageenableAnalyzer1112
Imageresizable1120
ListOnScrollVisibleContentChangeCallback1214
ListonItemDragStart814
NavDestinationtitle912
NavDestinationmode1112
NavDestinationbackButtonIcon11+1112
NavDestinationmenus12+1214
NavDestinationtoolbarConfiguration1320
NavDestinationonReady1120
NavDestinationonWillAppear1220
NavDestinationonWillDisappear1220
NavDestinationonWillShow1220
NavDestinationonWillHide1220
NavDestinationsystemBarStyle1220
NavDestinationonResult1522
NavDestinationbindToScrollable1422
NavDestinationbindToNestedScrollable1422
NavDestinationonActive1722
NavDestinationonInactive1722
NavDestinationonNewParam1922
Navigationtitle812
NavigationtoolbarConfiguration1020
NavigationcustomNavContentTransition1120
NavigationsystemBarStyle1220
NavigationenableVisibilityLifecycleWithContentCover2123
PatternLockbackgroundColor920
PatternLockonDotConnect1120
ProgressprivacySensitive1220
RefreshonOffsetChange1220
RichEditorcustomKeyboard1023
RichEditoronDidIMEInput1220
RichEditorenablePreviewText1218
RichEditorplaceholder1218
RichEditoronWillChange1218
RichEditoronDidChange1218
RichEditoreditMenuOptions1218
RichEditorenableKeyboardOnFocus1218
RichEditorenableHapticFeedback1320
RichEditorbarState1318
SelectmenuBackgroundColor1112
SelectmenuBackgroundBlurStyle1112
SwiperdisplayCount812
SymbolGlyphfontSize1112
SymbolGlyphfontColor1112
SymbolGlyphfontWeight1112
SymbolGlypheffectStrategy1112
SymbolGlyphrenderingStrategy1112
SymbolSpanfontSize1112
SymbolSpanfontColor1112
SymbolSpanfontWeight1112
SymbolSpaneffectStrategy1112
SymbolSpanrenderingStrategy1112
ScrollableCommonAttributeonWillScroll1214
ScrollableCommonAttributeonDidScroll1214
TabContentonWillShow1220
TabContentonWillHide1220
TabsedgeEffect1217
TabscustomContentTransition1120
TabsonContentWillChange1220
TabsbarBackgroundBlurStyle1112
TextAreaenterKeyType1112
TextenableHapticFeedback1318
TextInputshowCounter1112
TextInputonSecurityStateChange1220
TextPickeronScrollStop14+1420
TextPickeronScrollStop18+1820
TextTimertextShadow1112
TimePickerenableHapticFeedback1218
TimePickeronChange1820
VideoenableAnalyzer1220
VideoanalyzerConfig1220
VideoonError720
WaterFlowonScrollIndex1120