鸿蒙-深色模式适配

前言

在移动应用设计与开发领域,深色模式已从可选功能升级为用户体验的核心标配。它不仅能适配夜间使用场景、降低屏幕功耗与视觉疲劳,更能彰显应用的设计质感与人性化考量,成为衡量产品成熟度的重要指标。 本文聚焦手机应用开发中的深色模式适配实践,从设计原则、色彩体系构建、代码实现逻辑等维度展开解析,结合实际开发中的常见问题与优化方案,为开发者提供一套可落地的适配思路与技术参考,助力打造兼顾视觉体验与用户需求的高品质应用。

粗暴不适配

最粗暴的方法就是不适配,强制写死为亮色(浅色)模式. 在Ability 的 onCreate 方法中

typescript 复制代码
this.context
  .getApplicationContext()
  .setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_LIGHT);

这样,即使我们引用了一些系统资源,也不会在用户开启深色模式后随着改变。

适配

颜色适配

需要将之前硬编码在组件中的色值提取到 color.json文件中。

  1. 在base/element/color.json定义浅色模式颜色
  2. 在dark/element/color.json定义深色模式同名颜色
  3. 通过$r('app.color.resource_name')引用资源

这样在切换深色模式时,系统会根据当前模式自动匹配对应目录下的颜色值。 比如,在base/element/color.json文件中

json 复制代码
{
  "color": [
    {
      "name": "page_background_color",
      "value": "#ffffff"
    },
    {
      "name": "text_desc_color",
      "value": "#999999"
    },
    {
      "name": "text_subtitle_color",
      "value": "#666666"
    },
    {
      "name": "text_title_color",
      "value": "#333333"
    }
  ]
}

在dark/element/color.json文件中

json 复制代码
{
  "color": [
    {
      "name": "page_background_color",
      "value": "#000000"
    },
    {
      "name": "text_desc_color",
      "value": "#666666"
    },
    {
      "name": "text_subtitle_color",
      "value": "#999999"
    },
    {
      "name": "text_title_color",
      "value": "#cccccc"
    }
  ]
}

然后在代码引用

typescript 复制代码
Text(
  '在 resource 文件夹下新建 dark/element/color.json文件,同时在该文件中配置和 base/element/color.json文件中相同的颜色名称和不同的颜色值',
)
  .fontSize(14)
  .fontColor($r('app.color.text_desc_color'));
Text("在页面中应用配置文件中的色值,例如$r('app.color.text_title_color')")
  .fontSize(14)
  .fontColor($r('app.color.text_desc_color'));
Text('在手机中切换颜色模式,再返回应用')
  .fontSize(14)
  .fontColor($r('app.color.text_desc_color'));

媒体图片资源适配

静态图片适配:在 base/media 和 dark/media 目录放置同名图片文件,深色模式下系统优先加载 dark 目录的图片。 对于 svg 格式的图片,我们还可以使用 Image 组件的fillColor属性根据当前的颜色模式填充不同的颜色已达到适配的目的,但对于 svg 图片来讲,这个属性不是太好用,还不如再设计一张深色模式的svg 图片来的方便。

typescript 复制代码
Row() {
  Image($r('app.media.color_mode_icon')).width('50%')
  Column() {
    Image($r('app.media.color_mode_icon_svg')).height('50%').objectFit(ImageFit.Contain)
    Image($r('app.media.color_mode_icon_svg'))
      .height('50%')
      .fillColor($r('app.color.text_subtitle_color'))
      .objectFit(ImageFit.Contain)
  }.height(px2vp(1024)).width('50%').justifyContent(FlexAlign.Start)
}.width('100%').alignItems(VerticalAlign.Top)

状态栏适配

在 Ability 的onConfigurationUpdate回调中判断当前颜色模式,设置不同的状态栏颜色.

typescript 复制代码
  onConfigurationUpdate(newConfig: Configuration): void {
    this.setColorMode(newConfig.colorMode == ConfigurationConstant.ColorMode.COLOR_MODE_DARK)
  }

    setColorMode(isDark: boolean) {
    if (isDark) {
      window.getLastWindow(this.context).then((win) => {
        console.error('设置状态栏为深色模式')
        win.setWindowSystemBarProperties({
          statusBarColor: "#ff0000",
          statusBarContentColor: "#000000",

        }).catch((error:BusinessError) => {
          console.error('设置状态栏为深色模式 出错 ' + error.message)
        })
      })
    } else {
      window.getLastWindow(this.context).then((win) => {
        console.error('设置状态栏为浅色模式')
        win.setWindowSystemBarProperties({
          statusBarColor: "#00ff00",
          statusBarContentColor: "#ffffff",

        }).catch((error:BusinessError) => {
          console.error('设置状态栏为浅色模式 出错 ' + error.message)
        })
      })
    }
  }

当然也可以在onWindowStageCreate回调的windowStage.loadContent方法中判断一下当前的颜色模式,来设置初始状态

typescript 复制代码
windowStage.loadContent('pages/Index', (err) => {
  this.setColorMode(
    this.context.config.colorMode ==
      ConfigurationConstant.ColorMode.COLOR_MODE_DARK,
  );
});

webview适配

Web组件设置:通过 Web 组件的 .darkMode(WebDarkMode.Auto) 使网页跟随系统模式,或手动设置 .darkMode(WebDarkMode.On/Off)2。 强制深色转换:使用 .forceDarkAccess(true) 对未适配深色的网页进行色值算法转换(需注意部分颜色可能不符合预期)。

设置颜色模式

我们也可以通过代码的方式指定当前 app 使用哪种颜色模式

typescript 复制代码
Button('切换深色模式').onClick((_) => {
  this.getUIContext()
    .getHostContext()
    ?.getApplicationContext()
    .setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_DARK);
});
Button('切换浅色模式').onClick((_) => {
  this.getUIContext()
    .getHostContext()
    ?.getApplicationContext()
    .setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_LIGHT);
});
Button('跟随系统').onClick((_) => {
  this.getUIContext()
    .getHostContext()
    ?.getApplicationContext()
    .setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET);
});

效果

源码

github: github.com/huangyuanlo...

gitcode: gitcode.com/huangyuan_x...

相关推荐
马剑威(威哥爱编程)9 小时前
【共创稿事节】HarmonyOS 7 Agent 开发实战 11:并发多会话隔离
华为·harmonyos
行者-全栈开发13 小时前
【共创稿事节】HarmonyOS 7 空间音频实战:用 AudioSpatializationManager 构建“听得见方位“的声场
harmonyos·arkts·鸿蒙·空间音频·api 26·audio kit
马剑威(威哥爱编程)13 小时前
【共创稿事节】HarmonyOS 7 Agent 开发实战 03:Tool Calling 让 AI 查实时订单与库存
华为·harmonyos
倔强的石头10615 小时前
把 Typst 搬上开源鸿蒙 PC:一次“干净”的移植记录
华为·开源·harmonyos
梦想不只是梦与想17 小时前
鸿蒙 预览数据模拟:Hamock
harmonyos·模拟数据·hamock
贾伟康20 小时前
【化学实验室|04】HarmonyOS ArkTS 场景选择器实战:让 13 个离线实验的入口、筛选和返回路径保持一致
harmonyos·arkts·canvas·arkui·化学实验
特立独行的猫a21 小时前
仓颉语言GUI开发实战:用仓颉写串口/网络二合一调试助手
网络·ui·gui·harmonyos·tauri·仓颉
hqzing1 天前
把 glibc 移植到鸿蒙 PC 上:它能用来做什么
华为·harmonyos
OH_TPC1 天前
HarmonyOS APP开发---“习惯猫“习惯打卡App,需要用到这个库
华为·harmonyos·鸿蒙
熊猫钓鱼>_>2 天前
开源鸿蒙平台 KMP 三方库 kotlin-csv 适配全流程:从编码假设到沙箱真机互通验证
开发语言·人工智能·华为·kotlin·harmonyos·鸿蒙