
鸿蒙5深色模式适配:全局颜色变量的定义与应用
全局颜色变量的优势
一致性保障:统一管理所有颜色属性
高效开发:一处修改,全局生效
主题适配:无缝切换深浅模式
可维护性:清晰的颜色命名和结构
颜色资源文件定义
首先在 resources/base/element/color.json 中定义浅色模式的颜色变量:
{
“color”: [
{
“name”: “background_primary”,
“value”: “#FFFFFF”
},
{
“name”: “background_secondary”,
“value”: “#F5F5F7”
},
{
“name”: “text_primary”,
“value”: “#333333”
},
{
“name”: “text_secondary”,
“value”: “#666666”
},
{
“name”: “accent_color”,
“value”: “#007DFF”
},
{
“name”: “border_light”,
“value”: “#E0E0E0”
}
]
}
在 resources/dark/element/color.json 中定义深色模式的对应变量:
{
“color”: [
{
“name”: “background_primary”,
“value”: “#121212”
},
{
“name”: “background_secondary”,
“value”: “#1E1E1E”
},
{
“name”: “text_primary”,
“value”: “#E0E0E0”
},
{
“name”: “text_secondary”,
“value”: “#9E9E9E”
},
{
“name”: “accent_color”,
“value”: “#3399FF”
},
{
“name”: “border_light”,
“value”: “#383838”
}
]
}
动态主题管理器
创建 ThemeManager 类来管理主题切换逻辑:
// ThemeManager.java
package com.example.myapplication.theme;
import ohos.aafwk.ability.Ability;
import ohos.aafwk.ability.AbilitySlice;
import ohos.agp.utils.Color;
import ohos.app.Context;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;
import ohos.utils.zson.ZSONObject;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class ThemeManager {
private static final HiLogLabel LABEL = new HiLogLabel(HiLog.LOG_APP, 0x00201, “ThemeManager”);
private static ThemeManager instance;
private boolean isDarkMode = false;
private ThemeManager() {}
public static synchronized ThemeManager getInstance() {
if (instance == null) {
instance = new ThemeManager();
}
return instance;
}
// 加载当前主题设置
public void loadThemeSetting(Context context) {
try {
// 读取配置文件
String config = readAssetFile(context, "config.json");
ZSONObject configObj = ZSONObject.stringToZSON(config);
isDarkMode = configObj.getBoolean("darkMode");
} catch (IOException e) {
HiLog.error(LABEL, "Failed to load theme setting, using default: light mode");
isDarkMode = false;
}
}
// 切换主题
public void toggleTheme() {
isDarkMode = !isDarkMode;
HiLog.info(LABEL, "Theme switched to: " + (isDarkMode ? "Dark" : "Light"));
}
// 应用主题到整个应用
public void applyTheme(Ability ability) {
if (ability == null) {
return;
}
if (isDarkMode) {
// 应用深色主题资源
ability.setTheme(ResourceTable.Theme_App_Dark);
} else {
// 应用浅色主题资源
ability.setTheme(ResourceTable.Theme_App_Light);
}
}
public boolean isDarkMode() {
return isDarkMode;
}
private String readAssetFile(Context context, String fileName) throws IOException {
StringBuilder content = new StringBuilder();
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(context.getResourceManager().getRawFileEntry("resources/rawfile/" + fileName)
.openRawFile()))) {
String line;
while ((line = reader.readLine()) != null) {
content.append(line);
}
}
return content.toString();
}
}
在XML布局中应用全局颜色变量
在布局文件中,使用 $color:color_name 引用全局颜色变量:
<!-- ability_main.xml -->
<?xml version=“1.0” encoding=“utf-8”?>
<DirectionalLayout
xmlns:ohos=“http://schemas.huawei.com/res/ohos”
ohos:id=“$+id:main_container”
ohos:width=“match_parent”
ohos:height=“match_parent”
ohos:background_element=“$color:background_primary”
ohos:orientation=“vertical”
ohos:padding=“24vp”>
<Text
ohos:id="$+id:title"
ohos:width="match_content"
ohos:height="match_content"
ohos:text="应用设置"
ohos:text_color="$color:text_primary"
ohos:text_size="28fp"
ohos:text_alignment="center"
ohos:margin_bottom="24vp"/>
<DirectionalLayout
ohos:width="match_parent"
ohos:height="match_content"
ohos:background_element="$color:background_secondary"
ohos:orientation="horizontal"
ohos:corner_radius="16vp"
ohos:border_color="$color:border_light"
ohos:border_width="1vp"
ohos:padding="16vp">
<Text
ohos:width="0vp"
ohos:height="match_content"
ohos:text="主题设置"
ohos:text_color="$color:text_primary"
ohos:text_size="20fp"
ohos:layout_weight="1"/>
<Switch
ohos:id="$+id:theme_switch"
ohos:width="match_content"
ohos:height="match_content"/>
</DirectionalLayout>
<Button
ohos:id="$+id:action_button"
ohos:width="match_parent"
ohos:height="48vp"
ohos:text="执行操作"
ohos:background_element="$color:accent_color"
ohos:text_color="#FFFFFF"
ohos:text_size="18fp"
ohos:top_margin="24vp"/>
</DirectionalLayout>
在代码中访问颜色变量
在Java代码中动态获取颜色值:
// MainAbilitySlice.java
package com.example.myapplication.slice;
import com.example.myapplication.ResourceTable;
import com.example.myapplication.theme.ThemeManager;
import ohos.aafwk.ability.AbilitySlice;
import ohos.aafwk.content.Intent;
import ohos.agp.components.*;
import ohos.agp.utils.Color;
import ohos.app.Context;
import ohos.global.resource.NotExistException;
import ohos.global.resource.WrongTypeException;
import java.io.IOException;
public class MainAbilitySlice extends AbilitySlice {
private ThemeManager themeManager;
@Override
public void onStart(Intent intent) {
super.onStart(intent);
themeManager = ThemeManager.getInstance();
themeManager.loadThemeSetting(this);
setUIContent(ResourceTable.Layout_ability_main);
initComponents();
applyThemeStyles();
}
private void initComponents() {
Switch themeSwitch = (Switch) findComponentById(ResourceTable.Id_theme_switch);
themeSwitch.setChecked(themeManager.isDarkMode());
themeSwitch.setCheckedStateChangedListener((absButton, isChecked) -> {
themeManager.toggleTheme();
getAbility().reload();
});
Button actionButton = (Button) findComponentById(ResourceTable.Id_action_button);
actionButton.setClickedListener(component -> {
// 动态获取主题颜色
int accentColor = getThemeColor(this, ResourceTable.Color_accent_color);
getUITaskDispatcher().delayDispatch(() -> {
actionButton.setBackground(Color.argb(150, Color.red(accentColor),
Color.green(accentColor),
Color.blue(accentColor)));
}, 300);
getUITaskDispatcher().delayDispatch(() -> {
actionButton.setBackgroundColor(new Color(accentColor));
}, 600);
});
}
private void applyThemeStyles() {
Text title = (Text) findComponentById(ResourceTable.Id_title);
title.setTextColor(new Color(getThemeColor(this, ResourceTable.Color_text_primary)));
}
public static int getThemeColor(Context context, int colorResId) {
try {
return context.getResourceManager().getElement(colorResId).getColor();
} catch (IOException | NotExistException | WrongTypeException e) {
e.printStackTrace();
return 0xFF000000; // 黑色作为默认颜色
}
}
@Override
public void onActive() {
super.onActive();
}
@Override
public void onForeground(Intent intent) {
super.onForeground(intent);
}
}
深色模式适配最佳实践
语义化颜色命名:
使用类似 background_primary, text_secondary 等名称
避免使用具体颜色值命名,如 blue, gray_light
深浅模式一致性:
保持相同语义的颜色变量在两种模式下具有一致的作用
避免在深色模式中使用过强的对比度(推荐使用深灰而非纯黑)
动态主题切换优化:
@Override
public void onThemeChanged() {
// 检测主题变化时的优化处理
ComponentContainer root = (ComponentContainer) findComponentById(ResourceTable.Id_main_container);
root.removeAllComponents();
setUIContent(ResourceTable.Layout_ability_main);
applyThemeStyles();
}
可访问性考虑:
// 检查颜色对比度是否满足WCAG标准
public static boolean isAccessibleColor(int color1, int color2) {
// 实现对比度计算逻辑
return true; // 伪代码
}
组件状态颜色:
// 在color.json中
{
“name”: “button_background_pressed”,
“value”: “#0058CC”
}
<!-- 在组件背景中使用 -->
ohos:background_element
<state color=“$color:accent_color” state=“normal”/>
<state color=“$color:button_background_pressed” state=“pressed”/>
</ohos:background_element>
常见问题解决
主题切换不生效:
确保调用了 ability.setTheme() 方法
检查资源文件夹命名是否正确(base/dark)
颜色变量在XML中不生效:
检查变量名称是否拼写正确
确认资源文件中的 name 属性与XML引用一致
动态获取资源颜色为0:
检查资源ID是否正确
捕获处理 IOException, NotExistException, WrongTypeException
深色模式颜色过亮/过暗:
使用色彩空间转换工具确保相同视觉权重
Color.adjustLightness(int color, float factor) {
// 实现亮度调整算法
}
结论
鸿蒙5的深色模式适配通过全局颜色变量管理变得高效且系统化。关键点包括:
使用 color.json 定义语义化颜色变量
建立主题管理器统一控制主题切换
XML布局中使用 $color:variable 引用颜色
通过 getElement(colorResId).getColor() 动态获取颜色值
关注可访问性和组件状态管理
完整的全局颜色方案可以显著提高应用在深浅模式下的用户体验,同时降低后期维护成本。随着鸿蒙系统的不断更新,建议持续关注最新的主题适配API和最佳实践。
https://example.com/harmonyos-dark-light-example.png
(示例图:应用在浅色模式和深色模式下的对比效果)
