在鸿蒙应用开发中,界面主题管理是构建现代化应用的重要环节。ArkTS框架提供的主题换肤系统采用分层设计架构,主要包含三个核心层级:
这种分层结构使得主题管理既保持了全局一致性,又能满足局部定制需求。系统采用响应式设计,当主题发生变化时,所有使用主题Token的组件会自动触发UI更新,开发者无需手动处理重绘逻辑。
关键设计原则:主题系统遵循"约定优于配置"的理念,未显式定义的属性会自动继承上层主题值,这显著减少了重复配置的工作量。
创建自定义主题需要实现两个核心接口:
typescript复制interface CustomColors {
[key: string]: ResourceColor;
}
interface CustomTheme {
colors: CustomColors;
}
最佳实践建议采用类继承方式实现:
typescript复制export class AppColors implements CustomColors {
// 品牌主色(必须使用ResourceColor类型)
public brand: ResourceColor = '#FF007DFF';
// 使用系统资源引用(推荐方式)
public backgroundPrimary: ResourceColor = $r('sys.color.ohos_id_color_background_primary');
}
export class AppTheme implements CustomTheme {
public colors: AppColors = new AppColors();
// 可扩展其他主题属性
public fontSizes = {
small: 12,
medium: 16,
large: 20
};
}
在UIAbility的onWindowStageCreate回调中设置是最可靠的方式:
typescript复制windowStage.loadContent('pages/Index', (err) => {
if (!err) {
ThemeControl.setDefaultTheme(new AppTheme());
}
});
在Entry组件的aboutToAppear生命周期中设置:
typescript复制@Entry
@Component
struct MainPage {
aboutToAppear() {
ThemeControl.setDefaultTheme(new AppTheme());
}
}
重要限制:主题设置必须在UI渲染前完成,在build()之后调用将不会生效。
调用ThemeControl.setDefaultTheme(undefined)会清除自定义主题,恢复系统默认值。主题属性遵循原型链继承规则:
结合@State变量实现动态换肤:
typescript复制@Entry
@Component
struct ThemeDemo {
@State currentTheme: CustomTheme = new LightTheme();
build() {
Column() {
Button('切换主题')
.onClick(() => {
this.currentTheme = this.currentTheme instanceof LightTheme
? new DarkTheme()
: new LightTheme();
})
WithTheme({ theme: this.currentTheme }) {
// 主题敏感组件
}
}
}
}
建议采用AppStorage实现主题状态持久化:
typescript复制const THEME_KEY = 'app_current_theme';
// 保存主题
function saveTheme(theme: CustomTheme) {
AppStorage.SetOrCreate<CustomTheme>(THEME_KEY, theme);
}
// 读取主题
function loadTheme(): CustomTheme {
return AppStorage.Get<CustomTheme>(THEME_KEY) || new DefaultTheme();
}
在resources目录下建立深色模式资源:
code复制resources/
├── base
│ └── element/
│ └── color.json # 浅色模式资源
└── dark
└── element/
└── color.json # 深色模式资源
color.json示例:
json复制{
"color": [
{
"name": "text_primary",
"value": "#FF000000" // 浅色模式文本
}
]
}
使用WithTheme的colorMode属性实现局部控制:
typescript复制WithTheme({
colorMode: ThemeColorMode.DARK
}) {
// 此区域强制使用深色模式
}
支持的模式枚举:
ThemeColorMode.SYSTEM:跟随系统设置ThemeColorMode.LIGHT:强制浅色模式ThemeColorMode.DARK:强制深色模式系统预定义的色彩Token分为六大类别:
| 类别 | 示例Token | 使用场景 |
|---|---|---|
| 品牌色 | brand | 主按钮、重要标识 |
| 文本色 | fontPrimary | 主要文本内容 |
| 背景色 | backgroundPrimary | 页面背景 |
| 图标色 | iconPrimary | 功能图标 |
| 状态色 | warning | 警告、错误状态 |
| 交互色 | interactiveHover | 悬停、按压状态 |
| Token | 浅色值 | 深色值 | 透明度 |
|---|---|---|---|
| backgroundPrimary | #FFFFFF | #1A1A1A | 100% |
| fontPrimary | #000000 | #FFFFFF | 90% |
| fontSecondary | #000000 | #FFFFFF | 60% |
| divider | #000000 | #FFFFFF | 10% |
专业提示:使用带透明度的颜色值(如#1A000000)可以自动适配深浅模式,减少定制工作量。
时机问题:在UI渲染完成后才设置主题
资源路径错误:深色模式资源未放在dark目录
类型不匹配:自定义颜色值未使用ResourceColor类型
$r('app.color.xxx')或'#AARRGGBB'格式typescript复制@Observed
class ThemeStore {
currentTheme: CustomTheme = new AppTheme();
// 使用函数记忆优化计算
getColor = memoize((key: string) => {
return this.currentTheme.colors[key];
});
}
建议建立设计Token映射表:
| Figma变量名 | 鸿蒙Token | 默认值 |
|---|---|---|
| Primary/500 | brand | #FF007DFF |
| Neutral/800 | fontPrimary | #FF000000 |
| Danger/500 | warning | #FFFF0000 |
对于大型项目,推荐采用主题工厂模式:
typescript复制class ThemeFactory {
static getTheme(themeName: string): CustomTheme {
switch(themeName) {
case 'professional': return new ProfessionalTheme();
case 'vibrant': return new VibrantTheme();
default: return new DefaultTheme();
}
}
}
在实际项目开发中,我总结出几个关键经验点: