HarmonyOS深色模式适配实战——主题切换与WCAG对比度标准

技术栈:HarmonyOS 5.0 + ArkTS + AppStorage

适用场景:应用主题切换、无障碍适配、华为应用市场审核


前言

深色模式已成为现代应用的标配功能。华为应用市场审核对深色模式有严格要求,所有文字必须清晰可见。本文将介绍如何实现符合WCAG标准的深色模式适配。

一、WCAG对比度标准

WCAG(Web Content Accessibility Guidelines)定义了文字对比度标准:

级别 正常文本 大文本
AA级 ≥ 4.5:1 ≥ 3:1
AAA级 ≥ 7:1 ≥ 4.5:1

二、主题颜色定义

2.1 浅色主题

typescript 复制代码
export const LightTheme: ThemeColors = {
  pageBg: '#F8F9FA',
  cardBg: '#FFFFFF',
  cardBgSecondary: '#F5F5F5',
  
  // 文字色 - 符合WCAG标准
  textPrimary: '#1A1A1A',      // 对比度 16.1:1
  textSecondary: '#595959',    // 对比度 7.0:1
  textTertiary: '#737373',     // 对比度 4.6:1
  
  divider: '#EEEEEE',
  border: '#E5E5E5',
  buttonBg: '#F0F0F0',
  shadowColor: 'rgba(0, 0, 0, 0.08)',
};

2.2 深色主题

typescript 复制代码
export const DarkTheme: ThemeColors = {
  pageBg: '#121212',
  cardBg: '#1E1E1E',
  cardBgSecondary: '#2A2A2A',
  
  // 文字色 - 符合WCAG标准
  textPrimary: '#FFFFFF',      // 对比度 15.3:1
  textSecondary: '#CCCCCC',    // 对比度 9.7:1
  textTertiary: '#999999',     // 对比度 5.1:1
  
  divider: '#333333',
  border: '#404040',
  buttonBg: '#2A2A2A',
  shadowColor: 'rgba(0, 0, 0, 0.3)',
};

2.3 主题接口定义

typescript 复制代码
export interface ThemeColors {
  pageBg: string;
  cardBg: string;
  cardBgSecondary: string;
  textPrimary: string;
  textSecondary: string;
  textTertiary: string;
  divider: string;
  border: string;
  buttonBg: string;
  shadowColor: string;
}

三、主题管理器

typescript 复制代码
export class ThemeManager {
  private static isDarkMode: boolean = false;
  private static listeners: Array<(isDark: boolean) => void> = [];

  static setDarkMode(isDark: boolean): void {
    ThemeManager.isDarkMode = isDark;
    ThemeManager.listeners.forEach(listener => listener(isDark));
  }

  static getIsDarkMode(): boolean {
    return ThemeManager.isDarkMode;
  }

  static getTheme(): ThemeColors {
    return ThemeManager.isDarkMode ? DarkTheme : LightTheme;
  }

  static addListener(listener: (isDark: boolean) => void): void {
    ThemeManager.listeners.push(listener);
  }

  static removeListener(listener: (isDark: boolean) => void): void {
    const index = ThemeManager.listeners.indexOf(listener);
    if (index > -1) ThemeManager.listeners.splice(index, 1);
  }
}

四、页面中使用主题

4.1 使用AppStorage实现全局状态

typescript 复制代码
@Entry
@Component
struct MainPage {
  @StorageLink('appDarkMode') isDarkMode: boolean = true;
  
  getBgColor(): string { 
    return this.isDarkMode ? '#0D1117' : '#F5F5F5'; 
  }
  
  getTextPrimary(): string { 
    return this.isDarkMode ? '#FFFFFF' : '#1A1A1A'; 
  }
  
  getTextSecondary(): string { 
    return this.isDarkMode ? 'rgba(255,255,255,0.7)' : 'rgba(0,0,0,0.6)'; 
  }

  private toggleTheme(): void {
    this.isDarkMode = !this.isDarkMode;
    AppStorage.setOrCreate('appDarkMode', this.isDarkMode);
    PreferencesUtil.putBoolean('app_dark_mode', this.isDarkMode);
  }

  build() {
    Column() {
      Text('标题')
        .fontSize(24)
        .fontColor(this.getTextPrimary())
      
      Text('副标题')
        .fontSize(14)
        .fontColor(this.getTextSecondary())
      
      Button('切换主题')
        .onClick(() => this.toggleTheme())
    }
    .backgroundColor(this.getBgColor())
  }
}

4.2 跟随系统主题

typescript 复制代码
import ConfigurationConstant from '@ohos.app.ability.ConfigurationConstant';

@Entry
@Component
struct SettingsPage {
  @StorageProp('currentColorMode') @Watch('onColorModeChange') 
  currentColorMode: number = ConfigurationConstant.ColorMode.COLOR_MODE_LIGHT;
  
  @State themeMode: string = 'system'; // 'system' | 'light' | 'dark'

  onColorModeChange(): void {
    if (this.themeMode === 'system') {
      const isDark = this.currentColorMode === ConfigurationConstant.ColorMode.COLOR_MODE_DARK;
      AppStorage.setOrCreate('appDarkMode', isDark);
    }
  }

  private applyThemeMode(): void {
    let isDark: boolean;
    if (this.themeMode === 'system') {
      isDark = this.currentColorMode === ConfigurationConstant.ColorMode.COLOR_MODE_DARK;
    } else {
      isDark = this.themeMode === 'dark';
    }
    AppStorage.setOrCreate('appDarkMode', isDark);
    PreferencesUtil.putString('theme_mode', this.themeMode);
  }
}

五、对比度检查工具

推荐使用在线工具检查颜色对比度:

六、避坑指南

  1. 华为审核要求:深色模式下所有文字必须清晰可见
  2. @StorageLink vs @StorageProp:前者双向绑定,后者只读
  3. 颜色透明度 :使用rgba()时注意透明度对对比度的影响
  4. 图标适配:深色模式下图标也需要适配

总结

本文介绍了HarmonyOS深色模式的完整实现方案,包括符合WCAG标准的颜色定义、主题管理器、页面使用方法等。正确的深色模式适配不仅能通过应用市场审核,还能提升用户体验。

相关推荐
想你依然心痛13 小时前
HarmonyOS 6(API 23)实战:基于悬浮导航、沉浸光感与HMAF的“数智视界“——PC端AI智能体沉浸式数据可视化分析工作台
华为·ar·harmonyos·智能体
前端不太难21 小时前
从单页面到系统化:鸿蒙 App 演进路径
华为·状态模式·harmonyos
想你依然心痛1 天前
HarmonyOS 6(API 23)实战:基于悬浮导航、沉浸光感与HMAF的“文思智脑“——PC端AI智能体沉浸式智能写作工作台
人工智能·ar·harmonyos·ai写作
小雨青年1 天前
鸿蒙 HarmonyOS 6 | Pura X Max 鸿蒙原生适配 09:展开态列表增加字段但不变复杂
华为·harmonyos
richard_yuu1 天前
鸿蒙治愈游戏模块实战|四大轻量解压游戏、ArkTS动画交互与低功耗落地
游戏·交互·harmonyos
阿钱真强道1 天前
24 鸿蒙LiteOS GPIO中断实战:从原理到上升沿/下降沿详解
harmonyos·中断·rk·liteos·开源鸿蒙·瑞芯微·rk2206
cd_949217211 天前
鸿蒙系统下抖音存储空间不足怎么办?缓存清理教程
缓存·华为·harmonyos
轻口味1 天前
HarmonyOS 6.1 全栈实战录 - 14 渲染树透镜:FrameNode 渲染状态感知与高性能 UI 调优实战
ui·华为·harmonyos
HwJack201 天前
HarmonyOS NEXT 游戏APP开发中如何正确拦截退出手势
游戏·华为·harmonyos
HwJack201 天前
HarmonyOS APP开发中ArkTS/JS 类型错误全景拆解
javascript·华为·harmonyos