HarmonyOS 鸿蒙Next深色模式案例

发布于 1周前 作者 itying888 来自 鸿蒙OS

HarmonyOS 鸿蒙Next深色模式案例
<markdown _ngcontent-wfw-c149="" class="markdownPreContainer">

HarmonyOS Next应用开发案例(持续更新中……)

本案例完整代码,请访问:https://gitee.com/harmonyos-cases/cases/tree/master/CommonAppDevelopment/feature/fitfordarkmode

介绍

本示例介绍在开发应用以适应深色模式时,对于深色和浅色模式的适配方案,采取了多种策略如下:

  1. 固定属性适配:对于部分组件的颜色属性,如背景色或字体颜色,若保持不变,可直接设定固定色值或引用固定的资源文件。
  2. 双资源目录适配:在resources目录下新增dark子目录,用于存放深色模式下的特定颜色配置(color.json文件)和图片资源(media文件)。在深色模式下,系统会自动加载此目录中的颜色及图片资源,确保与浅色模式下的UI元素色彩差异性。
  3. 利用系统分层参数:对于支持深色模式切换的系统层级颜色资源,我们可以直接引用这些具有分层特性的参数,使得当切换设备主题时,相关组件的颜色能根据系统当前颜色模式自动更新。
  4. 监听当前颜色模式变化:通过注册AbilityStage.onConfigurationUpdate事件监听器,实时捕捉到设备深浅颜色模式的变化,并据此动态调整UI布局结构或逻辑处理,以适应不同模式下的最佳视觉体验。

效果图预览

使用说明

  1. 返回主页,进入系统设置切换深浅颜色模式,再点击深色模式适配进入切换后的颜色模式试图。应用启动并加载至首页时,用户点击深色模式适配按钮后,系统将根据当前设备的颜色模式展示相应的视图效果。
  2. 用户在返回主页后,再进入系统设置界面调整颜色模式(切换为深色或浅色模式),完成切换后再次点击首页的深色模式适配按钮,应用程序响应更改后的颜色模式对应的视图界面。

实现思路

  1. 当UI组件的颜色属性被设置为固定颜色值时,其在深色模式和浅色模式下的显示颜色将保持不变。
    // 将Text直接设置成'#000000'固定色值
    Text("精品好礼")
     .opacity(0.6)
     .fontColor($r('app.color.black_font_color'))
     .margin({ left: $r('app.integer.text_margin_left') })
    <button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 4px; right: 8px; font-size: 14px;">复制</button>
  2. 为了实现深色模式下的颜色适配,可以在resources目录下新建'dark/element'的子目录,并在此目录中创建color.json文件。为深色模式下的各个UI组件指定相应的颜色值,务必确保与浅色模式下同名颜色资源名称一致,以确保系统能够正确识别并切换。
    // 用资源ID方式设置Column背景色。(浅色模式色值为'#FA5A3C'、深色模式色值为'#000000')
    .backgroundColor($r('app.color.column_bg_color'))
    <button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 4px; right: 8px; font-size: 14px;">复制</button>
  3. 若UI组件的颜色属性引用的是系统提供的具有层级参数的颜色资源,则当设备在深色模式和浅色模式间切换时,这些颜色会自动调整至对应模式下的预设色值。
    // 用系统提供的分层参数颜色资源方式设置色值 
    Text(item.price)
     .fontSize($r('app.integer.goods_font'))
     .offset({ x: -3 })// 因为¥是中文字符,上面的是中文字符,占的宽度不一样,所以需要对齐,添加offset
     .fontColor($r('sys.color.ohos_id_color_foreground'))
    <button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 4px; right: 8px; font-size: 14px;">复制</button>
  4. 对于PNG、WEBP或JPEG格式的图片资源,若需支持深色模式,应在resources目录下新增一个'dark/media'子目录,将深色模式下对应的图片放入此目录,并确保图片文件名与浅色模式下的图片相同,以便系统根据当前模式加载合适的图片资源。
    // SVG格式图片fillColor颜色资源ID方式设置(浅色模式色值为'#000000'、深色模式色值为'#FFFFFF')
    Image($r('app.media.view'))
     .fillColor($r('app.color.view_fill_color'))
     .width($r('app.integer.view_image_width'))
     .aspectRatio(1)
     .objectFit(ImageFit.Contain)
    <button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 4px; right: 8px; font-size: 14px;">复制</button>
  5. 在存在深浅两种模式下布局结构或逻辑处理有所差异的情况时,开发者应当利用AbilityStage.onConfigurationUpdate监听接口来实时感知系统主题的变化,并据此做出相应的布局调整或逻辑处理,从而确保应用能够在不同模式下呈现出理想的界面效果及功能体验。
  • 第一步保存全局参数,并通过onConfigurationUpdate刷新状态栏
// 获取当前的颜色模式并保存并在onConfigurationUpdate
AppStorage.setOrCreate('currentColorMode', this.context.config.colorMode);
// 保存windowStage供fitfordarkmode的har包中FitForDarkPage.ets中setStatusBar方法修改状态栏颜色。
AppStorage.setOrCreate('windowStage', windowStage);

// 检测当前的深浅模式是否发生变化,刷新状态栏 onConfigurationUpdate(config: Configuration) { // 获取最新的变更颜色并更新到AppStorage AppStorage.setOrCreate(‘currentColorMode’, config.colorMode); logger.info(onConfigurationUpdate, config: ${<span class="hljs-built_in">JSON</span>.stringify(config)}); } <button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 4px; right: 8px; font-size: 14px;">复制</button>

  • 第二步在FitForDarKMode.ets中获取并监听当前颜色模式
// [@StorageProp](/user/StorageProp) + [@Watch](/user/Watch) 获取并监听当前颜色模式
[@StorageProp](/user/StorageProp)('currentColorMode') [@Watch](/user/Watch)('onColorModeChange') currentMode: number = 0;
// [@Watch](/user/Watch)回调函数,监听颜色模式刷新状态变量
onColorModeChange(): void {
  if (this.currentMode === ConfigurationConstant.ColorMode.COLOR_MODE_DARK) {
    this.banner = $r("app.media.dark_mode_banner");
  } else {
    this.banner = $r("app.media.light_mode_banner");
  }
}
<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 4px; right: 8px; font-size: 14px;">复制</button>
  • 第三步在FitForDarKMode.ets生命周期aboutToAppear中根据当前颜色模式刷新banner状态变量,切换不同的图片。
// 在自定义组件生命周期aboutToAppear中,根据当前颜色模式刷新banner状态变量,切换不同的图片。
aboutToAppear(): void {
  if (this.currentMode === ConfigurationConstant.ColorMode.COLOR_MODE_DARK) {
    this.banner = $r("app.media.dark_mode_banner");
} else {
    this.banner = $r("app.media.light_mode_banner");
    // 在当前为浅色模式中,确保界面美观且颜色统一,设置导航栏的背景色。
    setStatusBar(ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET);
  }
}
<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 4px; right: 8px; font-size: 14px;">复制</button>
  • 第四步在FitForDarKMode.ets生命周期aboutToDisappear中,重置导航栏的背景色避免影响其它页面的导航栏为红色。
// 在自定义组件生命周期aboutToDisappear中,重置导航栏的背景色避免影响其它页面的导航栏为红色。
aboutToDisappear(): void {
  setStatusBar(this.currentMode)
}
<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 4px; right: 8px; font-size: 14px;">复制</button>
  • 第五步在FitForDarKMode.ets生命周期aboutToDisappear中,重置导航栏的背景色避免影响其它页面的导航栏为红色。
// 调用setWindowSystemBarProperties()设置状态栏及导航栏的颜色
windowClass.setWindowSystemBarProperties(sysBarProps, (err) => {
  if (err.code) {
    logger.error('Failed to set the system bar properties. Cause: ' + JSON.stringify(err));
    return;
  }
  logger.info('Succeeded in setting the system bar properties.');
});
<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 4px; right: 8px; font-size: 14px;">复制</button>

高性能知识点

不涉及

工程结构&模块类型

fitfordarkmode                                   // har类型
|---mock
|   |---GoodsMock.ets                            // 商品列表数据 
|---mode
|   |---GoodsModel.ets                           // 商品数据类型定义 
|---view
|   |---FitForDarkMode.ets                       // 深色模式适配主页面 
|   |---GoodsList.ets                            // 商品列表自定义组件 
<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 4px; right: 8px; font-size: 14px;">复制</button>

模块依赖

本实例依赖common模块来实现资源的调用以及公共组件FunctionDescription的引用。 还需要依赖EntryAbility.ets模块

参考资料

设置深色模式

@ohos.app.ability.ConfigurationConstant (ConfigurationConstant)

</markdown>
11 回复

我在使用 双资源目录适配   时,创建了dark文件夹,设置了相应颜色资源,为何切换系统颜色模式改为深色后读取的还是浅色资源,

而且 onConfigurationUpdate 没有回调,更改任何系统配置都不能收到回调

这里面有些方法在api9中报错,是next上面新增的吗?
// 在自定义组件生命周期aboutToAppear中,根据当前颜色模式刷新banner状态变量,切换不同的图片。
  aboutToAppear(): void {
    let applicationContext = getContext(this).getApplicationContext();
    applicationContext.setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET);
    this.banner = setBanner(this.currentMode);
  }

// 在自定义组件生命周期aboutToDisappear中,重置导航栏的背景色避免影响其它页面的导航栏为红色。 aboutToDisappear(): void { let applicationContext = getContext(this).getApplicationContext(); applicationContext.setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_LIGHT); setStatusBar(ConfigurationConstant.ColorMode.COLOR_MODE_LIGHT); }<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>

这里面的 

applicationContext.setColorMode()方法是报错的;

  // 1.获取应用主窗口。
let windowClass: window.Window;
let windowStage: window.WindowStage = AppStorage.get(‘windowStage’) as window.WindowStage;<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>

提示 没有 AppStorage.get方法

是的,Next版本才有

双资源目录适配 的方案成本比较低,但有一个问题需要请教。

假如应用中的日夜间模式有3个开关,1. 强制亮色 2. 强制暗色 3. 跟随系统

跟随系统使用 “双资源目录适配”是完全没问题的,那 强制亮色 和 强制暗色,这两种不跟随系统的该如何低成本的实现呢?

在 EntryAblility.ets中可以强制设置单色模式 // 全局设置为浅色模式 let applicationContext = this.context.getApplicationContext(); applicationContext.setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_LIGHT);

我在EntryAblility.ets使用了你的建议,用的是DARK,模拟器显示用的资源依然是base文件夹的,不是dark文件夹的。IDE版本5.0.3.403,运行时17.0.10,SDK NEXT Beta1

今天终于搞懂了。必须要使用模拟器或者真机才生效,我一直用的Preview,所以一直不行。用了模拟器,一切OK了。

// 全局设置为浅色模式

let applicationContext = this.context.getApplicationContext(); applicationContext.setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_LIGHT);

// 将状态栏和导航栏的背景色设置为跟应用窗口相同的颜色
await windowClass.setWindowSystemBarProperties({
navigationBarColor: "#00FF00",
statusBarColor: "#00FF00",
navigationBarContentColor: "#00FF00",
statusBarContentColor: "#00FF00"
})

以上函数都不支持跨平台, 有没有其他方法实现?

作为IT专家,对于HarmonyOS鸿蒙Next的深色模式案例,以下是一些专业解答:

HarmonyOS鸿蒙Next深色模式的设置相对简单,用户可以通过系统设置菜单进入“显示和亮度”,然后选择“深色模式”,并可选择“定时开启”或“全天开启”。此外,用户还可以选择哪些应用开启深色模式。

在开发应用以适应深色模式时,采取了多种策略。例如固定属性适配,对于部分组件的颜色属性,如背景色或字体颜色,若保持不变,可直接设定固定色值或引用固定的资源文件。还有双资源目录适配,在resources目录下新增dark子目录,用于存放深色模式下的特定颜色配置和图片资源,系统会自动加载此目录中的资源,确保与浅色模式下的UI元素色彩有差异性。

同时,利用系统分层参数也是关键。对于支持深浅模式切换的系统层级颜色资源,可以直接引用这些具有分层特性的参数,使得当切换设备主题时,相关组件的颜色能根据系统当前颜色模式自动更新。

此外,通过监听当前颜色模式变化,开发者可以实时捕捉到设备深浅颜色模式的变化,并据此动态调整UI布局结构或逻辑处理,以适应不同模式下的最佳视觉体验。

总之,HarmonyOS鸿蒙Next深色模式的实现涉及多方面的技术和策略。如需深入了解或遇到具体问题,请查阅相关开发文档或资源。如果问题依旧没法解决请联系官网客服,官网地址是:https://www.itying.com/category-93-b0.html

回到顶部