HarmonyOS鸿蒙Next中如何监听系统主题变化并动态切换 UI?

HarmonyOS鸿蒙Next中如何监听系统主题变化并动态切换 UI? 用户在系统设置里切换深色模式后,我的 App 界面颜色没变。鸿蒙有没有类似 iOS 的 traitCollection 或 Android 的 Configuration.uiMode 来监听主题变更?

5 回复

深色模式(Dark Mode)又称为暗色模式,是与日常应用使用过程中的浅色模式(Light Mode)相对应的一种UI主题。深色模式最早来源于人机交互领域的研究和实践,该模式并非简单地将页面背景变为黑色,文字内容变为白色,而是提供一整套适配深色模式的应用配色主题。深色模式相较浅色模式更加柔和,能减少亮度对用户眼睛造成的刺激和疲劳,此外深色模式能在一定程度上降低应用功耗,提升续航表现。

应用深色模式适配,需遵循基本的UX设计原则,保障应用页面内容的易读性、舒适性和一致性,具体可参考深色模式设计原则。应用适配过程主要包含字体颜色、元素背景色等颜色资源的适配,媒体资源如图片图标的适配,以及系统状态栏的适配,此外需要对一些特殊情况如使用了Web组件加载的Web页面进行处理。

实现原理

当系统切换到深色模式后,应用内可能会出现部分内容切换到深色主题的情况,例如状态栏、弹窗背景色、系统控件等,会导致应用内页面效果错乱。

为应对上述情况,需要对应用进行深色模式下的内容适配,目前该适配主要依靠资源目录。当系统对应的设置项发生变化后(如系统语言、深浅色模式等),应用会自动加载对应资源目录下的资源文件。

系统为深色模式预留了dark目录,该目录在应用创建时默认不存在,在进行深色模式适配时,需要开发者在src/main/resources中手动创建出dark目录,将深色模式所需的资源放置到该目录下。对于浅色模式所需的资源,可以放入默认存在的src/main/resources/base目录下。

说明

在进行资源定义时,需要在base目录与dark目录中定义同名的资源。例如在base/element/color.json文件中定义text_color为黑色,在dark/element/color.json文件中定义text_color为白色,那么当深浅色切换时,应用内使用了$(‘app.color.text_color’)作为颜色值的元素会自动切换到对应的颜色,而无需使用其他逻辑判断进行控制。

一般情况下深浅色模式切换不会导致应用界面产生结构上的变化,而是页面结构一致但是采用不同的主题配色、配图等,使得整个应用在切换到深色模式后依然保持自然美观,以下为深色模式适配的UX示例。

图1 深色模式适配UX示例图

cke_1741.png

从上图中可以看到,在应用进行深色模式适配过程中主要的适配项有颜色资源适配、媒体资源适配、状态栏适配,除此之外若应用内使用了Web组件加载的Web页面,那么还需对Web页面适配深色模式,具体适配方案可点击对应链接跳转到具体章节查看。

目前业内应用向用户提供的深浅色模式切换有以下两种常见方式。

  • 应用跟随系统深浅色模式切换 实现上,需要开发者使用setColorMode()方法将ColorMode设置为COLOR_MODE_NOT_SET(未设置颜色模式),然后应用在运行过程中就可以自动感知到系统颜色模式切换,若应用完成了深浅色模式适配,将自动切换到对应的颜色模式。
  • 应用内提供手动控制深浅色的开关供用户自行选择 实现上,切换深色模式需要调用setColorMode()方法将ColorMode设置为COLOR_MODE_DARK(深色模式),切换浅色模式需要将ColorMode设置为COLOR_MODE_LIGHT(浅色模式),这样就可以完成对应用深浅色的手动控制。

综上分析,深色模式适配内容如下表所示。

表1 深色模式适配内容

适配项 适配内容 适配方式
颜色资源适配 组件背景色,字体颜色等 1. 使用受支持的系统资源
2. 使用color.json资源文件
媒体资源适配 应用内使用到的图片、图标等 1. SVG类型图标可使用fillColor()属性
2. 使用media资源目录
状态栏适配 深浅模式下不同的状态栏表现,包括状态栏的背景色以及状态栏内时间等内容的字体颜色 1. 对应用背景色进行深浅色适配
2. 根据当前深浅色状态动态设置状态栏字体颜色
Web内容适配 应用内使用Web组件加载的Web页面 参考Web组件设置深色模式

在EntryAbility中获取并维护当前深浅色状态,在onCreate时将当前colorMode放在AppStorage中,并在配置变化的onConfigurationUpdate()回调中动态更新深浅色状态。

export default class EntryAbility extends UIAbility {
  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    AppStorage.setOrCreate<ConfigurationConstant.ColorMode>('currentColorMode', this.context.config.colorMode);
    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate');
  }
  // ...
  onConfigurationUpdate(newConfig: Configuration): void {
    const currentColorMode: ConfigurationConstant.ColorMode | undefined = AppStorage.get('currentColorMode');
    if (currentColorMode !== newConfig.colorMode) {
      AppStorage.setOrCreate<ConfigurationConstant.ColorMode>('currentColorMode', newConfig.colorMode);
    }
  }
}

更多详细内容详见开发文档:https://developer.huawei.com/consumer/cn/doc/best-practices/bpta-dark-mode-adaptation

更多关于HarmonyOS鸿蒙Next中如何监听系统主题变化并动态切换 UI?的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


当系统环境变量发生变化时(语言、颜色模式等)会触发Ability的 onConfigurationUpdate 生命周期回调,其中的 colorMode 就是更改之后的颜色模式。

export default class EntryAbility extends UIAbility {
  onConfigurationUpdate(newConfig: Configuration): void {
    // newConfig.colorMode 就是当前系统颜色模式 `0`: dark, `1`: light
    hilog.info(0x0000, 'EntryAbility', `#onConfigurationUpdate():the newConfig.colorMode is ${newConfig.colorMode}`)
  }
}

参考:

这个:Environment:设备环境查询,可以调用这个Api来查看当前设备的环境,然后来适配深色模式啥的。

在HarmonyOS Next中,监听系统主题变化可使用themeManager模块。通过themeManager.on('themeChange')注册监听器,回调中获取当前主题标识(如'dark''light')。在UI组件中,使用资源引用(如$r('app.string.theme_dependent_value'))或条件判断动态应用对应主题的资源ID。ArkTS声明式UI会自动响应主题变更,无需手动刷新界面。

在HarmonyOS Next中,监听系统主题变化并动态切换UI,主要通过 ConfigurationManagerColorManager 来实现。系统提供了完整的主题监听与资源动态响应机制。

核心步骤如下:

  1. 获取并监听系统主题配置 使用 ConfigurationManager 来获取当前的主题模式(colorMode),并订阅其变化。

    import { configurationManager } from '@kit.ConfigurationKit';
    import { BusinessError } from '@kit.BasicServicesKit';
    
    // 获取当前主题模式
    let currentColorMode: configurationManager.ColorMode = configurationManager.getColorMode();
    console.log(`Current color mode: ${currentColorMode}`); // 0: 浅色模式, 1: 深色模式
    
    // 订阅主题变化事件
    try {
      configurationManager.on('colorModeChange', (newColorMode: configurationManager.ColorMode) => {
        console.log(`Color mode changed to: ${newColorMode}`);
        // 在此回调中处理UI更新逻辑
        this.updateUIForTheme(newColorMode);
      });
    } catch (error) {
      const err: BusinessError = error as BusinessError;
      console.error(`Failed to listen for color mode changes. Code: ${err.code}, message: ${err.message}`);
    }
    
  2. 定义多主题资源 在项目的 resources 目录下,为不同主题定义颜色等资源。

    • resources/base/element/color.json: 定义基础颜色资源(默认/浅色主题)。
    • resources/dark/element/color.json: 定义深色主题下的颜色资源。 系统会根据当前的 colorMode 自动匹配并加载对应目录下的资源。
  3. 在UI中应用主题资源 在ArkUI(声明式范式)中,直接引用资源ID即可,系统会自动根据当前主题切换。

    @Entry
    @Component
    struct Index {
      build() {
        Column() {
          Text('Hello Theme')
            .fontSize($r('app.float.title_font_size')) // 引用尺寸资源
            .fontColor($r('app.color.primary_text')) // 引用颜色资源,会自动适配主题
        }
        .width('100%')
        .height('100%')
        .backgroundColor($r('app.color.background')) // 背景色也会自动适配
      }
    }
    
  4. 动态更新UI(如需手动控制) 如果在主题变化回调中需要执行更复杂的逻辑(如重新加载特定数据、切换图片资源等),可以在监听回调 updateUIForTheme 方法中实现。

    private updateUIForTheme(mode: configurationManager.ColorMode): void {
      // 1. 可以手动设置组件的状态或变量
      // this.isDarkMode = (mode === configurationManager.ColorMode.COLOR_MODE_DARK);
    
      // 2. 关键:强制刷新使用了主题资源的UI。
      // 对于@State修饰的变量,改变其值可以触发UI更新。
      // 例如,定义一个@State colorVar: Resource = $r('app.color.primary_text');
      // 在回调中执行 this.colorVar = $r('app.color.primary_text'); 即可触发重绘。
    
      // 3. 或者使用ArkUI提供的状态管理机制,将主题模式注入到整个UI树。
    }
    

总结: HarmonyOS Next 的主题切换是系统级行为。开发者只需:

  • 正确配置多主题资源文件。
  • 在UI中引用资源ID($r('app.color.xxx'))。
  • 通过 configurationManager.on('colorModeChange', ...) 监听变化,并在必要时触发UI更新。

系统会自动处理大部分资源切换。对于需要代码响应的场景,上述监听回调提供了入口。这与iOS的 traitCollectionDidChange 和Android的 UiModeManager 监听在概念和效果上是对等的。

回到顶部