当前位置: 首页 > news >正文

【HarmonyOS 6.1 全场景实战】《灵犀厨房》实战(二十五):【深色模式】一键切换暗色主题——让 App 在深夜也温柔

HarmonyOS 6.1 全场景实战|《灵犀厨房》实战(二十五):【深色模式】一键切换暗色主题——让 App 在深夜也温柔

摘要:从第1篇到现在,我们写了上万行代码,但有一个细节一直被忽略——用户可能在深夜打开 App,白色背景的刺眼光线会让体验大打折扣。本篇将利用 HarmonyOS 6.1.0 的资源限定符(Resource Qualifier)setColorMode()API,为《灵犀厨房》构建深色模式——只需在resources/dark/目录添加一组颜色覆盖值,配合一个 Toggle 开关,App 就能在浅色和深色之间无感切换。


一、引言:被忽略的“夜间访客”

你是否有过这样的体验:深夜关灯准备刷一下手机,打开某个 App,瞬间被白色背景刺得眯起眼睛。《灵犀厨房》也存在同样的问题——从第1篇到现在,我们所有的代码都默认运行在浅色模式下,硬编码了#FFF8F0#FFFFFF#333333等颜色。这在白天没问题,但在深夜,这些颜色就是“光污染”。

深色模式不只是“把白底换成黑底”。它需要解决三个核心问题:

问题浅色模式深色模式需求
背景亮度暖米色/纯白,柔和舒适深灰/纯黑,减少眩光
文字对比度深色文字在浅色背景上浅色文字在深色背景上,但仍需保持足够对比度
主题色一致性#FF6B35橙红色醒目需要调亮为#FF8C5A,否则在深色背景上辨识度下降

🎯本篇目标:用最少的工作量(28个颜色定义 + 1个Toggle开关 + 2处演示替换),为《灵犀厨房》装上深色模式,并为后续的全面迁移建立基础设施。


二、核心原理:HarmonyOS 的资源限定符机制

HarmonyOS 的资源系统支持限定符目录。当你写下:

.backgroundColor($r('app.color.bg_page'))

ArkUI 会根据当前系统的颜色模式,自动去resources/base/resources/dark/目录中查找同名资源。

resources/ ├── base/element/color.json ← 默认值(浅色模式) │ { "name": "bg_page", "value": "#FFF8F0" } │ └── dark/element/color.json ← 深色模式覆盖值 { "name": "bg_page", "value": "#121212" }

关键机制同名覆盖。dark 目录中只放需要改变的颜色。不需要覆盖的颜色(如text_white: #FFFFFF)不出现在 dark 目录中,系统自动沿用 base 的值。这极大减少了维护成本——你只需要定义“变化的部分”。

LIGHT

DARK

backgroundColor
($r('app.color.bg_page'))

资源解析器

当前 ColorMode?

base/element/color.json
bg_page: #FFF8F0

dark/element/color.json
bg_page: #121212

图一解读:资源解析器是 HarmonyOS 的资源查找引擎。它根据系统当前的ColorMode自动选择对应限定符目录下的资源。开发者只需在代码中写$r('app.color.bg_page'),不需要写任何if (isDark)判断。这种声明式的资源管理是 HarmonyOS 相比传统 Android 开发的一大优势。


三、颜色语义化设计:从“颜色值”到“设计意图”

直接写#FF6B35有两个问题:一是无法被深色模式覆盖(硬编码颜色不受资源系统管理),二是含义模糊——三个月后你自己都忘了这个颜色是干嘛用的。我们需要定义一套语义化颜色名,让颜色名表达用途而非色值

语义名浅色值暗色值用途
primary#FF6B35#FF8C5A主题色(按钮、标签、强调文字)
primary_light#FFF0E6#3D2010主题色浅底(卡片内强调区域)
bg_page#FFF8F0#121212页面背景
bg_card#FFFFFF#1E1E1E卡片背景
bg_smart_screen#1A1A2E#0D0D0D智慧屏深色背景
bg_secondary#F8F9FA#1A1A1A次级背景(列表、分组头)
text_primary#333333#E0E0E0主文字
text_secondary#666666#B0B0B0次要文字
text_hint#999999#808080提示文字
text_white#FFFFFF#FFFFFF白字(深色底上使用,无需覆盖)
divider#F0F0F0#2A2A2A分割线
success#4CAF50#66BB6A成功/在线
warn#FF9800#FFB74D警告/待机
error#F44336#EF5350错误/离线

命名原则

  • 用途命名(bg_page),而非颜色值(orange)——方便后续换主题色
  • 暗色值调亮主色#FF6B35#FF8C5A),保证深色背景上的对比度
  • 暗色值降低背景亮度#FFFFFF#1E1E1E),减少眩光
  • text_white不覆盖——白色在深色背景上不变,缺省即自动沿用 base

四、实战步骤

Step 1:定义颜色资源文件

resources/base/element/color.json(新增 14 个颜色):

{"color":[{"name":"primary","value":"#FF6B35"},{"name":"primary_light","value":"#FFF0E6"},{"name":"bg_page","value":"#FFF8F0"},{"name":"bg_card","value":"#FFFFFF"},{"name":"bg_smart_screen","value":"#1A1A2E"},{"name":"bg_secondary","value":"#F8F9FA"},{"name":"text_primary","value":"#333333"},{"name":"text_secondary","value":"#666666"},{"name":"text_hint","value":"#999999"},{"name":"text_white","value":"#FFFFFF"},{"name":"divider","value":"#F0F0F0"},{"name":"success","value":"#4CAF50"},{"name":"warn","value":"#FF9800"},{"name":"error","value":"#F44336"}]}

resources/dark/element/color.json(新增 13 个暗色覆盖,不含text_white):

{"color":[{"name":"primary","value":"#FF8C5A"},{"name":"primary_light","value":"#3D2010"},{"name":"bg_page","value":"#121212"},{"name":"bg_card","value":"#1E1E1E"},{"name":"bg_smart_screen","value":"#0D0D0D"},{"name":"bg_secondary","value":"#1A1A1A"},{"name":"text_primary","value":"#E0E0E0"},{"name":"text_secondary","value":"#B0B0B0"},{"name":"text_hint","value":"#808080"},{"name":"divider","value":"#2A2A2A"},{"name":"success","value":"#66BB6A"},{"name":"warn","value":"#FFB74D"},{"name":"error","value":"#EF5350"}]}

注意text_white不出现在 dark 目录中——白色在深色背景上不需要变化,系统自动沿用 base 的值。

Step 2:添加 Toggle 切换开关

ProfilePage中添加一个 Switch 开关:

import{common,ConfigurationConstant}from'@kit.AbilityKit';@LocalisDark:boolean=false;aboutToAppear():void{constctx=this.getUIContext().getHostContext()ascommon.UIAbilityContext;this.isDark=ctx.config.colorMode===ConfigurationConstant.ColorMode.COLOR_MODE_DARK;}// UIRow(){Text(this.isDark?'🌙 深色模式':'☀️ 浅色模式').fontSize(14).fontColor('#333')Blank()Toggle({type:ToggleType.Switch,isOn:this.isDark}).selectedColor('#FF6B35').onChange((on:boolean)=>{this.isDark=on;constctx=this.getUIContext().getHostContext()ascommon.UIAbilityContext;constmode=on?ConfigurationConstant.ColorMode.COLOR_MODE_DARK:ConfigurationConstant.ColorMode.COLOR_MODE_LIGHT;ctx.getApplicationContext().setColorMode(mode);})}
所有页面资源解析器ApplicationContextToggle 开关👤 用户所有页面资源解析器ApplicationContextToggle 开关👤 用户点击切换setColorMode(DARK)通知颜色模式变更$r('app.color.xxx')全部重新解析所有页面立即切换为深色

图二解读setColorMode()是全局生效的——一旦调用,所有使用了$r('app.color.xxx')的组件会立刻重新解析为 dark 目录的值。无需手动刷新任何页面,无需遍历组件树。这是 HarmonyOS 声明式资源系统最大的优势:状态变更 → 自动响应。

Step 3:渐进式迁移——只改两行演示

当前代码中大量使用了硬编码颜色(如'#FF6B35''#333''#FFF')。全部替换成本高且风险大。本篇采用「渐进式迁移」策略:

当前状态
100% 硬编码颜色

第25篇
+ 14个颜色资源
+ Toggle 开关
+ 2处演示替换

后续篇章
每次改文件顺带替换
硬编码 → $r()

目标状态
核心路径100%资源化
老旧代码逐步替换

图三解读:本篇文章只完成第一步——建立颜色基础设施,并替换两处关键页面作为验证。后续每改一个文件就顺手把硬编码颜色替换为$r(),最终实现全量迁移。这种策略避免了“一次性大重构”的风险,也与开发节奏自然融合。

本篇只替换了两处作为演示:

// Index.ets —— 页面背景.backgroundColor($r('app.color.bg_page'))// 原 '#FFF8F0'// DeviceControlCard.ets —— 卡片背景.backgroundColor($r('app.color.bg_card'))// 原 Color.White

验证效果

  • ✅ 浅色模式:Index 背景#FFF8F0(暖米色),设备卡片#FFFFFF(纯白)
  • ✅ 深色模式:Index 背景#121212(深灰),设备卡片#1E1E1E(浅黑)
  • ✅ 切换 Toggle 后,两处颜色立即变化,其余硬编码颜色保持不变

运行截图


五、代码增删改清单

文件新增/修改说明
resources/base/element/color.json修改新增 14 个语义化颜色值(浅色默认)
resources/dark/element/color.json修改新增 13 个暗色覆盖值
pages/ProfilePage.ets修改新增Toggle深色模式开关(~25行)
pages/Index.ets修改页面背景改为$r('app.color.bg_page')(2行)
components/DeviceControlCard.ets修改卡片背景改为$r('app.color.bg_card')(1行)

六、设计决策

决策选择理由
颜色命名方式语义化(bg_page)而非色值(orange换主题色时只需改资源文件,不碰代码
暗色主色#FF8C5A(比浅色#FF6B35更亮)深色背景上需要更高对比度的颜色才醒目
迁移策略渐进式(只替换2处演示,其余逐步)全量替换 300+ 处硬编码风险大,后续每改一个文件顺带替换
text_white是否覆盖否(dark 目录不含此色)白色在深色背景上不需要变化,缺省即沿用 base
切换入口位置ProfilePage(设置页)符合用户预期——"设置"中切换主题
aboutToAppear读取当前模式ctx.config.colorMode读取保证 Toggle 初始状态与系统当前模式一致

七、本阶段总结与下篇预告

本篇是《灵犀厨房》系列中“代码量最少、视觉冲击最大”的一篇:

  • 14 个颜色资源:覆盖主题色、背景、文字、分割线、状态色五大类
  • 1 个 Toggle 开关:浅色/深色一键切换,全局即时生效
  • 渐进式迁移:不追求一次性全替换,2 处演示 + 后续逐文件迁移
  • 零破坏:硬编码颜色继续工作,与新资源引用共存

深色模式不是“把白底换成黑底”——主色要调亮以保持对比度,分割线要降低到仅可见,背景层级要用不同深度的灰色区分。一个好的深色模式,用户切换后感觉“本该如此”。

下篇预告:第 26 篇《响应式布局:折叠屏与平板完美适配》。让《灵犀厨房》在手机、折叠屏、平板上都展现最佳布局,让同一个 App 在不同屏幕尺寸上都有量身定做的体验。我们下一篇见!


📚 本系列持续更新中:下一篇将实现跨设备响应式布局,让 App 自由穿梭于手机、平板、折叠屏。
🔗专栏入口:[《HarmonyOS6.1全场景实战》合集]

📦 获取基线版本源码包:包括第1-15篇所有代码 + 架构文档 + Flask 后端

如果你觉得这篇文章对您有所帮助,麻烦您动动发财之手点赞 👍、收藏 ⭐ 和评论 💬。谢谢大家!!

http://www.rkmt.cn/news/1457643.html

相关文章:

  • 不止于HSV:探索Halcon中trans_from_rgb支持的10+种颜色空间(CIELab、YUV等)及应用场景
  • 深入解读VMware日志:从‘disk error while paging’错误码看虚拟机内存管理机制
  • CAPL数据处理避坑指南:当心byte数组转Hex字符串时这些隐藏的字节序和内存问题
  • 2026年更新:河北螺旋钢管知名企业弘冠管道综合实力深度解析 - 2026年企业资讯
  • 【稀缺首发】Gartner未公开的AI治理成熟度评估矩阵(含17项工具集成得分卡)
  • 微针人机界面:无创生物传感与智能给药的前沿技术解析
  • FreeRTOS 手动移植教程(二):任务管理——多任务创建、优先级抢占与删除
  • 从‘暴力破解’到‘算法还原’:深度解析super_mega_protection.exe的密钥校验逻辑
  • Cadence 16.6老用户的福音:Library Builder汉化版详细菜单解读与配置实战
  • 互联网大厂Java面试:从Spring框架到微服务场景的技术问答
  • 一高科技集团三大业务布局助力教育高质量发展
  • 别再手动传证书了!K8s里用cert-manager自动管理TLS证书的保姆级教程
  • 别扔!全志A13老平板变身Linux小主机:Armbian镜像制作与Lima开源GPU驱动实战
  • 如何快速部署通达信缠论可视化插件:5步完整实战指南
  • 别再死记硬背!用‘客户服务系统’实战案例,5分钟搞懂UML类图怎么画
  • 5个颠覆性策略掌握MediaCreationTool.bat:突破Windows 11硬件限制的完整解决方案
  • 大模型微调实战指南:从技术原理到Qwen多模型矩阵的工程
  • 遥感新手必看:用Python+ENVI快速识别植被、水体、裸土(附光谱曲线对比图)
  • AI工具如何秒级生成公平抽奖结果:3种主流LLM+RNG融合方案实测对比(含代码)
  • 别再只重启服务器了!深度解析百度云加速522错误的三种根源与长效优化方案
  • 2026乡镇同城服务创业攻略:从选址到落地全流程搭建方案
  • 告别寄存器恐惧:用Arduino+PlatformIO一步步调通SX1262 LoRa收发(附完整代码)
  • 出海企业技术架构优化实地观察 拆解AWS Lambda无服务器的落地细节
  • 用MATLAB跑通胎儿心电提取:LMS自适应滤波实操包,含原始数据和效果对比图
  • 长转短这条工程链路里,最容易被低估的瓶颈是什么
  • 告别踩坑!在Visual Studio 2013下编译Eclipse Paho MQTT C库的保姆级指南(含SSL编译失败解决方案)
  • 别再乱下DLL了!用Dependency Walker深度排查.pyd文件依赖问题的正确姿势
  • 2026年6月亳州黄金回收白银回收铂金回收权威可靠门店 TOP5 排行榜+联系方式电话
  • YOLO11涨点优化:蒸馏结构 | 基于ReviewKD(回顾式知识蒸馏),多层特征渐进对齐,轻量YOLO11精度跃升
  • OV摄像头SCCB协议实战:从I2C老司机到图像传感器配置的避坑指南