HarmonyOS鸿蒙Next中Ability如何响应系统深色模式切换而不重启?

HarmonyOS鸿蒙Next中Ability如何响应系统深色模式切换而不重启? 用户在设置中切换主题,我的 App 页面重建了,导致正在编辑的内容丢失。能不能只换颜色不重建页面?

5 回复

开发者您好,这边在应用中写了一个简单的输入框,然后输入文字后,在设置->显示和亮度切换深色模式,应用界面并没有被刷新,这边页面是怎么写的呢?有没有复线的demo和步骤可以提供下。

@Entry
@Component
struct Index{
  build() {
    Column(){
      TextInput()
    }
  }
}

更多关于HarmonyOS鸿蒙Next中Ability如何响应系统深色模式切换而不重启?的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


小伙伴你好,可以在 EntryAbility 中使用 onConfigurationUpdate 事件进行系统深/浅色主题的切换监听。

示例代码:

import { AbilityStage, Configuration } from '@kit.AbilityKit'; // 必须导入 Configuration 类型

export default class MyAbilityStage extends AbilityStage {
  onConfigurationUpdate(newConfig: Configuration): void {
    console.info(`onConfigurationUpdated, colorMode: ${newConfig.colorMode}`);
  }
}

参考文档

你是按照官方的适配文档弄的吗?

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

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

本文主要将介绍深色模式的适配过程,同时会列举出适配过程中的常见问题及解决方案。

实现原理

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

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

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

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

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

cke_718.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组件设置深色模式

详见官方文档:https://developer.huawei.com/consumer/cn/doc/best-practices/bpta-dark-mode-adaptation

在HarmonyOS Next中,Ability可通过订阅Configuration配置变化事件来响应深色模式切换。使用onConfigurationUpdate回调监听colorMode配置变更,无需重启Ability即可动态更新UI主题。

在HarmonyOS Next中,Ability可以通过监听系统主题变化事件来响应深色模式切换,无需重建页面或重启应用。核心方法是使用UI上下文提供的on('themeChange')事件监听器。

具体实现步骤如下:

  1. 在Ability的UIAbility或ExtensionAbility中获取UI上下文,通常在onWindowStageCreate生命周期回调中执行。
  2. 注册主题变化监听:通过windowStage.getMainWindow().getUIContext()获取UIContext对象,然后调用其on('themeChange')方法注册监听器。
  3. 在监听器回调中更新界面:当系统主题切换时,监听器被触发。在此回调中,你可以根据新的主题(例如,通过UIContextgetTheme()方法获取当前主题标识)动态更新当前页面的颜色资源,而不会导致页面重建。
  4. 在合适的时机取消监听:例如在onWindowStageDestroy中,调用off('themeChange')取消监听,避免内存泄漏。

关键点在于,你的应用界面应使用ArkUI的状态管理(如@State)来绑定颜色资源。当主题切换事件触发后,你只需更新这些状态变量,ArkUI框架会自动计算差异并仅更新发生变化的UI组件,从而实现无缝主题切换。

示例代码片段示意:

// 在Page或自定义组件中
import { UIContext } from '@kit.ArkUI';

@Entry
@Component
struct Index {
  private uiContext: UIContext | null = null;
  // 使用状态变量绑定颜色
  @State currentBgColor: ResourceColor = $r('app.color.background');

  aboutToAppear() {
    // 获取UIContext
    this.uiContext = getUIContext();
    // 注册监听
    this.uiContext?.on('themeChange', () => {
      // 根据新的主题更新颜色状态变量
      this.currentBgColor = this.getDynamicColor();
    });
  }

  aboutToDisappear() {
    // 取消监听
    this.uiContext?.off('themeChange');
  }

  // 根据当前主题返回对应颜色
  private getDynamicColor(): ResourceColor {
    // 获取当前主题逻辑,例如通过this.uiContext.getTheme()
    // 返回对应的ResourceColor
    return ...;
  }

  build() {
    // UI组件绑定状态变量
    Column() {
      // ...
    }
    .backgroundColor(this.currentBgColor)
  }
}

通过以上方式,当系统主题切换时,仅会触发颜色状态的更新和UI的重绘,Ability和Page都不会发生重建,从而有效避免了编辑内容丢失的问题。你需要确保应用内使用的颜色值都通过资源引用($r)或根据主题动态计算的方式定义,而不是硬编码。

回到顶部