HarmonyOS 鸿蒙Next中@Styles装饰器
HarmonyOS 鸿蒙Next中@Styles装饰器 [ArkUI 声明式 UI 中,@Styles 装饰器如何实现样式与主题的动态绑定?](https://segmentfault.com/q/1010000047526849)
【解决方案】
开发者您好,您这边是否是设置应用内主题换肤,希望@Styles 装饰器设置的涉及主题色的属性也会随主题动态变换,可以通过设置theme.color.***值来实现@Styles跟随主题动态变化
如:
[@Styles](/user/Styles)
fancy() {
.width('200')
.backgroundColor(this.myTheme.colors?.backgroundEmphasize);
}
完整项目代码参考官方示例:主题颜色自定义。
将src/main/ets/pages/DisplayPage.ets替换为以下代码:
/*
*
* * Copyright (c) 2025 Huawei Device Co., Ltd.
* * Licensed under the Apache License, Version 2.0 (the "License");
* * you may not use this file except in compliance with the License.
* * You may obtain a copy of the License at
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
*
*/
import { CustomTheme, PromptAction, window } from '@kit.ArkUI';
import { common, ConfigurationConstant } from '@kit.AbilityKit';
import { gAppTheme, gCustomTheme1 } from '../common/AppColors';
import { ThemeItem } from '../components/ThemeItem';
import { StyleConstants, TOGGLE_LIST } from '../constants/StyleConstants';
import { EyeProtectionMode } from '../util/EyeProtectionMode';
@Entry
@Component
export struct DisplayPage {
@StorageProp('topRectHeight') topRectHeight: number = StyleConstants.ZERO;
brightnessValue: number = StyleConstants.INITIAL_BRIGHTNESS;
brightnessMax: number = StyleConstants.BRIGHTNESS_MAX;
toggleList: boolean[] = TOGGLE_LIST;
fontPrimary: ResourceColor | undefined = gAppTheme?.colors?.fontPrimary;
@State myTheme: CustomTheme = gCustomTheme1;
@State isCustomTheme: boolean = false;
context = this.getUIContext().getHostContext() as common.UIAbilityContext;
windowStage: window.WindowStage = AppStorage.get('windowStage') as window.WindowStage;
mainWin: window.Window = this.windowStage.getMainWindowSync();
promptAction: PromptAction = this.getUIContext().getPromptAction();
aboutToAppear(): void {
this.topRectHeight = this.getUIContext().px2vp(AppStorage.get('topRectHeight'));
// 修改brightness即可改变屏幕亮度
let brightness = this.brightnessValue / StyleConstants.BRIGHTNESS_MAX;
this.mainWin.setWindowBrightness(brightness);
}
@Builder
myBuilder(title: string, hasToggle: boolean, index: number, message?: string) {
Column() {
Row() {
Text(title)
.fontSize($r('app.float.font_size_18'))
.fontColor(Color.Black)
.layoutWeight(1);
if (hasToggle) {
Toggle({ type: ToggleType.Switch, isOn: this.toggleList[index] })
.alignSelf(ItemAlign.Center)
.width($r('app.float.toggle_width'))
.height($r('app.float.toggle_height'))
.onClick(() => {
this.toggleList[index] = !this.toggleList[index];
this.handleOnclick(index);
});
}
}
.width(StyleConstants.FULL_WIDTH);
if (message) {
Row() {
Text(message)
.width($r('app.float.message_width'))
.fontSize($r('app.float.font_size_12'))
.fontColor($r('app.color.message_color'));
}
.width(StyleConstants.FULL_WIDTH)
.margin({
top: $r('app.float.margin_5')
});
}
}
.padding({
top: $r('app.float.margin_18'),
bottom: $r('app.float.margin_16'),
left: $r('app.float.margin_16'),
right: $r('app.float.margin_16')
});
}
@Builder
myDivider() {
Divider()
.strokeWidth(StyleConstants.DIVIDER_STROKE_WIDTH)
.width(StyleConstants.DIVIDER_WIDTH)
.color($r('app.color.divider_color'));
}
[@Styles](/user/Styles)
fancy() {
.width('200')
.backgroundColor(this.myTheme.colors?.backgroundEmphasize);
}
build() {
WithTheme({ theme: this.myTheme }) {
Column({ space: StyleConstants.INDEX_SPACE }) {
Button('测试主题色').fancy()
.fontColor(Color.Black)
Row() {
Text($r('app.string.theme'))
.fancy()
.fontSize($r('app.float.font_size_26'))
.fontWeight(FontWeight.Bold)
.fontColor(Color.Black)
}
.width(StyleConstants.FULL_WIDTH)
.height($r('app.float.theme_height'))
.alignItems(VerticalAlign.Bottom);
Row() {
ThemeItem({
img: $r('app.media.img_system'),
title: StyleConstants.SYSTEM_THEME,
color: this.isCustomTheme ? $r('app.color.unselect_color') : $r('app.color.system_color'),
radio: this.isCustomTheme ? $r('app.media.dot') : $r('app.media.system_dot')
})
.margin({
right: $r('app.float.theme_between')
})
.onClick(() => {
this.isCustomTheme = false;
this.myTheme = gCustomTheme1;
});
ThemeItem({
img: $r('app.media.img_custom'),
title: StyleConstants.CUSTOM_THEME,
color: this.isCustomTheme ? $r('app.color.custom_color') : $r('app.color.unselect_color'),
radio: this.isCustomTheme ? $r('app.media.custom_dot') : $r('app.media.dot')
})
.onClick(() => {
this.isCustomTheme = true;
this.myTheme = gAppTheme;
});
}
.width(StyleConstants.FULL_WIDTH)
// .backgroundColor(Color.White)
.borderRadius(StyleConstants.BORDER_RADIUS)
.padding({
top: $r('app.float.margin_16'),
bottom: $r('app.float.margin_16'),
left: $r('app.float.theme_left_padding'),
right: $r('app.float.theme_right_padding')
});
Column() {
this.myBuilder(StyleConstants.BRIGHTNESS, false, StyleConstants.NO_TOGGLE);
Row() {
Image($r('app.media.bright'))
.width($r('app.float.img_bright_small_width'))
.height($r('app.float.img_bright_small_height'));
Slider({ value: this.brightnessValue, max: this.brightnessMax })
.trackThickness($r('app.float.slider_track_thickness'))
.blockBorderWidth(StyleConstants.BLOCK_BORDER_WIDTH)
.blockBorderColor(this.isCustomTheme ? $r('app.color.custom_color') : $r('app.color.system_color'))
.blockSize({
width: $r('app.float.slider_block_size_width'),
height: $r('app.float.slider_block_size_height'),
})
.layoutWeight(1)
.onChange((value) => {
this.brightnessValue = value;
this.mainWin.setWindowBrightness(value / StyleConstants.BRIGHTNESS_MAX);
});
Image($r('app.media.bright'))
.width($r('app.float.img_bright_big_width'))
.height($r('app.float.img_bright_big_height'));
}
.width(StyleConstants.FULL_WIDTH)
.padding({
left: $r('app.float.margin_16'),
right: $r('app.float.margin_16'),
bottom: $r('app.float.margin_5')
})
.margin({
top: $r('app.float.slider_top_margin')
});
this.myDivider();
this.myBuilder(StyleConstants.AUTO_BRIGHTNESS_TITLE, true, StyleConstants.AUTO_BRIGHTNESS);
}
.width(StyleConstants.FULL_WIDTH)
// .backgroundColor(Color.White)
.borderRadius(StyleConstants.BORDER_RADIUS);
Column() {
this.myBuilder(StyleConstants.FULL_SCREEN_APPS, false, StyleConstants.NO_TOGGLE);
this.myDivider();
this.myBuilder(StyleConstants.EYE_PROTECTION_TITLE, true, StyleConstants.EYE_PROTECTION);
this.myDivider();
this.myBuilder(StyleConstants.ADAPTIVE_TINT_TITLE, true, StyleConstants.ADAPTIVE_TINT,
StyleConstants.ADAPTIVE_TINT_MESSAGE);
}
.width(StyleConstants.FULL_WIDTH)
// .backgroundColor(Color.White)
.borderRadius(StyleConstants.BORDER_RADIUS);
Column() {
this.myBuilder(StyleConstants.DISABLE_COLOR_MODE_TITLE, true, StyleConstants.DISABLE_COLOR_MODE);
}
.width(StyleConstants.FULL_WIDTH)
// .backgroundColor(Color.White)
.borderRadius(StyleConstants.BORDER_RADIUS);
}
// .backgroundColor($r('app.color.custom_color'))
.width(StyleConstants.FULL_WIDTH)
.height(StyleConstants.FULL_HEIGHT)
.padding({
left: $r('app.float.margin_17'),
right: $r('app.float.margin_17'),
top: this.topRectHeight
});
};
}
handleOnclick(id: number) {
switch (id) {
case StyleConstants.EYE_PROTECTION:
if (this.toggleList[StyleConstants.EYE_PROTECTION]) {
EyeProtectionMode.getInstance().createSubWithEyeWindow(StyleConstants.EYE_PROTECTION_COLOR);
} else {
EyeProtectionMode.getInstance().removeSubWithEyeWindow();
}
break;
case StyleConstants.DISABLE_COLOR_MODE:
if (this.toggleList[StyleConstants.DISABLE_COLOR_MODE]) {
this.context.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_LIGHT);
} else {
this.context.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET);
this.promptAction.showToast({ message: $r('app.string.close_color_mode') });
}
break;
case StyleConstants.AUTO_BRIGHTNESS:
this.promptAction.showToast({ message: $r('app.string.only_display') });
break;
case StyleConstants.ADAPTIVE_TINT:
this.promptAction.showToast({ message: $r('app.string.only_display') });
}
}
}
可以看到@Styles配置的属性能够跟随主题变化。
更多关于HarmonyOS 鸿蒙Next中@Styles装饰器的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
(如果你问的主题色配置)
配置主题色的时候,不是已经手动在 CustomColors 的实现类里面配置了颜色了嘛。
使用 ThemeControl.setDefaultTheme() 方法切换主题色的之后会触发自定义组件的 onWillApplyTheme() 生命周期回调。
可以通过其中的 theme 参数来获取当前主题色的色值。用一个状态变量接受之后就可以在其他地方用了。
可以参考文档 深色模式适配 - 颜色资源适配 在 @Styles 内使用 $r("") 去引入对于的颜色资源即可实现。
在HarmonyOS Next的ArkUI声明式UI框架中,@Styles装饰器主要用于定义可复用的组件样式集,但它本身并不直接提供与系统主题(如浅色/深色模式)的动态绑定机制。@Styles定义的是静态样式规则。
要实现样式与主题的动态绑定,需要结合资源管理和状态管理:
-
核心方法:使用资源引用与
@State- 在
resources/base/element/和resources/dark/element/等目录下,分别定义不同主题下的颜色、尺寸等资源值(如color.json)。 - 在组件中使用
$r('app.color.my_text_color')等形式引用这些资源。系统会根据当前主题自动匹配对应的资源值。 - 对于需要运行时动态切换的样式(非系统主题切换),可以结合
@State装饰的状态变量。在@Styles定义的样式中,可以使用this.状态变量名来引用动态值。
- 在
-
示例:动态颜色绑定
// 在@Styles中引用状态变量 @Styles function customStyle() { .backgroundColor(this.myDynamicColor) // this指向组件实例 .width(100) .height(100) } @Entry @Component struct Index { @State myDynamicColor: Resource = $r('app.color.primary_color') // 初始值引用资源 build() { Row() { // 应用样式,颜色可随myDynamicColor改变而动态更新 Text('Hello') .customStyle() } .onClick(() => { // 点击切换颜色,触发UI更新 this.myDynamicColor = $r('app.color.secondary_color') }) } } -
系统主题监听 若要响应系统主题(如深色模式)切换,需使用
configuration模块的onConfigurationUpdate回调,在主题变化时更新相关的状态变量或资源引用。
总结:@Styles负责封装样式规则,而动态绑定依赖于:
- 资源系统:实现跨主题的静态资源适配。
- 状态变量(如
@State):将动态值传入@Styles。 - 配置监听:响应系统级主题变化。
因此,样式与主题的动态绑定是一个组合使用资源引用、状态管理和@Styles封装的过程,而非由@Styles独立完成。

