news 2026/7/18 4:19:31

及时做APP开发实战(十)-深浅色主题适配实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
及时做APP开发实战(十)-深浅色主题适配实现

及时做APP开发实战(十)-深浅色主题适配实现

本文将详细介绍如何在HarmonyOS NEXT中实现深浅色主题适配,让应用跟随系统主题动态切换。

一、需求背景

1.1 功能需求

现代应用需要支持深浅色主题切换,提供更好的用户体验:

┌─────────────────────────────────────┐ │ 主题适配需求 │ ├─────────────────────────────────────┤ │ • 跟随系统主题自动切换 │ │ • 动态更新所有UI组件颜色 │ │ • 保持主题切换的流畅性 │ │ • 抽离颜色常量便于维护 │ └─────────────────────────────────────┘

1.2 技术方案

HarmonyOS提供Configuration.colorMode监听系统主题变化:

模式说明
COLOR_MODE_LIGHT浅色模式
COLOR_MODE_DARK深色模式
COLOR_MODE_NOT_SET跟随系统

二、主题颜色设计

2.1 颜色体系规划

【图1:颜色体系】

┌─────────────────────────────────────┐ │ 颜色分类 │ ├─────────────────────────────────────┤ │ 背景色 │ │ - 页面背景 background │ │ - 卡片背景 cardBackground │ │ │ │ 文本色 │ │ - 主文本 textPrimary │ │ - 次要文本 textSecondary │ │ - 提示文本 textHint │ │ │ │ 主题色 │ │ - 主色 primary │ │ - 主色浅 primaryLight │ │ - 成功色 success │ │ - 警告色 warning │ │ │ │ 边框色 │ │ - 边框 border │ │ - 分割线 divider │ └─────────────────────────────────────┘

2.2 ThemeColors接口定义

exportinterfaceThemeColors{// 背景色background:string;cardBackground:string;// 文本色textPrimary:string;textSecondary:string;textHint:string;// 主题色primary:string;primaryLight:string;success:string;warning:string;// 边框色border:string;divider:string;}

2.3 浅色主题配置

constLightTheme:ThemeColors={background:'#F5F5F5',cardBackground:'#FFFFFF',textPrimary:'#333333',textSecondary:'#666666',textHint:'#999999',primary:'#FF6B6B',primaryLight:'#FFE5E5',success:'#4CAF50',warning:'#FF9800',border:'#E0E0E0',divider:'#EEEEEE'};

2.4 深色主题配置

constDarkTheme:ThemeColors={background:'#1A1A1A',cardBackground:'#2D2D2D',textPrimary:'#FFFFFF',textSecondary:'#B0B0B0',textHint:'#808080',primary:'#FF6B6B',primaryLight:'#3D2D2D',success:'#66BB6A',warning:'#FFB74D',border:'#404040',divider:'#333333'};

三、ThemeUtil工具类实现

3.1 类结构设计

┌─────────────────────────────────────┐ │ ThemeUtil │ ├─────────────────────────────────────┤ │ - instance: ThemeUtil │ │ - currentTheme: ThemeColors │ │ - isDarkMode: boolean │ ├─────────────────────────────────────┤ │ + getInstance(): ThemeUtil │ │ + updateTheme(config): void │ │ + getColors(): ThemeColors │ │ + isDark(): boolean │ │ + getBackgroundColor(): string │ │ + getCardBackground(): string │ │ + getTextPrimary(): string │ │ + ...其他getter方法 │ └─────────────────────────────────────┘

3.2 完整实现代码

importConfigurationConstantfrom'@ohos.app.ability.ConfigurationConstant';import{Configuration}from'@kit.AbilityKit';// 主题颜色接口(同上)exportinterfaceThemeColors{...}// 主题配置(同上)constLightTheme:ThemeColors={...};constDarkTheme:ThemeColors={...};exportclassThemeUtil{privatestaticinstance:ThemeUtil|null=null;privatecurrentTheme:ThemeColors=LightTheme;privateisDarkMode:boolean=false;// 私有构造函数privateconstructor(){}// 获取单例publicstaticgetInstance():ThemeUtil{if(!ThemeUtil.instance){ThemeUtil.instance=newThemeUtil();}returnThemeUtil.instance;}/** * 根据配置更新主题 */publicupdateTheme(configuration:Configuration):void{try{constcolorMode=configuration.colorMode;this.isDarkMode=colorMode===ConfigurationConstant.ColorMode.COLOR_MODE_DARK;this.currentTheme=this.isDarkMode?DarkTheme:LightTheme;console.info(`[ThemeUtil] 主题已更新:${this.isDarkMode?'深色':'浅色'}`);}catch(err){consterrorMsg=errinstanceofError?err.message:String(err);console.error(`[ThemeUtil] 更新主题失败:${errorMsg}`);}}/** * 获取当前主题颜色 */publicgetColors():ThemeColors{returnthis.currentTheme;}/** * 是否深色模式 */publicisDark():boolean{returnthis.isDarkMode;}// 其他getter方法...publicgetBackgroundColor():string{returnthis.currentTheme.background;}publicgetCardBackground():string{returnthis.currentTheme.cardBackground;}publicgetTextPrimary():string{returnthis.currentTheme.textPrimary;}publicgetTextSecondary():string{returnthis.currentTheme.textSecondary;}publicgetTextHint():string{returnthis.currentTheme.textHint;}publicgetPrimary():string{returnthis.currentTheme.primary;}publicgetSuccess():string{returnthis.currentTheme.success;}publicgetBorder():string{returnthis.currentTheme.border;}}

四、Ability中监听主题变化

4.1 在EntryAbility中添加监听

import{AbilityConstant,ConfigurationConstant,UIAbility,Want,Configuration}from'@kit.AbilityKit';import{ThemeUtil}from'../utils/ThemeUtil';exportdefaultclassEntryAbilityextendsUIAbility{onCreate(want:Want,launchParam:AbilityConstant.LaunchParam):void{// 设置跟随系统主题try{this.context.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET);}catch(err){hilog.error(DOMAIN,'testTag','Failed to set colorMode');}}/** * 监听系统配置变化(包括主题变化) */onConfigurationUpdate(newConfig:Configuration):void{hilog.info(DOMAIN,'testTag','Configuration updated');// 更新主题try{ThemeUtil.getInstance().updateTheme(newConfig);}catch(err){hilog.error(DOMAIN,'testTag','Failed to update theme');}}}

4.2 配置变化流程

【图2:主题切换流程】

┌─────────────────────────────────────┐ │ 主题切换流程 │ ├─────────────────────────────────────┤ │ 用户切换系统主题 │ │ ↓ │ │ 系统发送Configuration变化事件 │ │ ↓ │ │ EntryAbility.onConfigurationUpdate │ │ ↓ │ │ ThemeUtil.updateTheme() │ │ ↓ │ │ 更新currentTheme和isDarkMode │ │ ↓ │ │ UI组件响应式更新 │ └─────────────────────────────────────┘

五、页面中使用主题

5.1 定义主题状态变量

import{ThemeUtil,ThemeColors}from'../utils/ThemeUtil';@Entry@Componentstruct PomodoroPage{// 主题状态@StatethemeColors:ThemeColors=ThemeUtil.getInstance().getColors();@StateisDarkMode:boolean=false;// 工具类privatethemeUtil:ThemeUtil=ThemeUtil.getInstance();// 生命周期asyncaboutToAppear(){// 初始化主题this.themeColors=this.themeUtil.getColors();this.isDarkMode=this.themeUtil.isDark();}}

5.2 在UI组件中应用主题颜色

@BuilderTopBar(){Row(){Text('🍅 番茄钟').fontSize(24).fontWeight(FontWeight.Bold).fontColor('#FFFFFF')}.width('100%').height(56).backgroundColor(this.isBreak?this.themeColors.success:this.themeColors.primary)}@BuilderTimerSection(){Column(){// 进度条Progress({value:this.getProgress(),total:100,type:ProgressType.Ring}).color(this.isBreak?this.themeColors.success:this.themeColors.primary).backgroundColor(this.themeColors.border)// 时间显示Text(this.formatTime(this.timeLeft)).fontSize(64).fontWeight(FontWeight.Bold).fontColor(this.themeColors.textPrimary)}.width('100%').backgroundColor(this.themeColors.background)}@BuilderPomodoroStats(){Column(){Row(){Column(){Text(this.pomodoroCount.toString()).fontSize(28).fontWeight(FontWeight.Bold).fontColor(this.themeColors.primary)Text('今日完成').fontSize(12).fontColor(this.themeColors.textSecondary)}}}.backgroundColor(this.themeColors.cardBackground)}

5.3 半模态弹窗主题适配

.bindSheet($$this.showTimePicker,this.TimePickerSheet(),{height:300,backgroundColor:this.isDarkMode?'#2D2D2D':Color.White,dragBar:true,onWillDismiss:()=>{this.showTimePicker=false;}})

六、颜色对比效果

6.1 浅色主题效果

【图3:浅色主题】

6.2 深色主题效果

【图4:深色主题】

七、最佳实践

7.1 颜色使用规范

// ✅ 推荐:使用主题颜色.backgroundColor(this.themeColors.background).fontColor(this.themeColors.textPrimary)// ❌ 不推荐:硬编码颜色.backgroundColor('#F5F5F5').fontColor('#333333')

7.2 状态变量设计

// ✅ 推荐:使用@State自动响应@StatethemeColors:ThemeColors=ThemeUtil.getInstance().getColors();// ❌ 不推荐:普通变量不会触发更新privatethemeColors:ThemeColors=ThemeUtil.getInstance().getColors();

7.3 主题颜色扩展

// 扩展主题颜色接口exportinterfaceThemeColors{// 基础颜色...// 扩展颜色error:string;// 错误色info:string;// 信息色disabled:string;// 禁用色}// 在主题配置中添加constLightTheme:ThemeColors={// 基础颜色...error:'#F44336',info:'#2196F3',disabled:'#BDBDBD'};

八、总结

本文实现了番茄钟应用的深浅色主题适配:

  1. 颜色体系设计:定义完整的ThemeColors接口
  2. 工具类封装:ThemeUtil单例类管理主题状态
  3. 系统监听:在Ability中监听配置变化
  4. 响应式更新:使用@State实现UI自动更新

主题适配是现代应用的基本要求,合理的设计可以让应用在各种环境下都有良好的显示效果。


版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/18 4:18:20

B站用户行为数据分析架构演进与ClickHouse优化实践

1. 项目背景与核心挑战在当今互联网产品运营中,用户行为数据分析已成为驱动业务决策的关键引擎。作为国内领先的年轻人文化社区,B站面临着海量用户行为数据处理的严峻挑战:每天产生数千亿条行为事件,数据增量超过10TB,…

作者头像 李华
网站建设 2026/7/18 4:15:08

MR-D02多组分气液动态配气仪在高校实验室的应用场景与选型建议

高校实验室配气设备选型的挑战与解决方案高校实验室在气敏传感器研究、VOC检测、环境监测、红外光谱仪研发等领域,需要使用配气仪制备标准混合气体。然而,传统的纯气体配气仪无法满足液态有机溶剂的配气需求,导致实验受限。多组分气液动态配气…

作者头像 李华
网站建设 2026/7/18 4:13:06

影刀RPA 文件上传与下载的自动化处理

影刀RPA 文件上传与下载的自动化处理 作者:林焱 文件上传和下载看着简单——不就是点个按钮选个文件、或者点个链接保存个文件吗?但真到了自动化里,这俩操作坑多到你怀疑人生。上传按钮点击后弹出来的是系统文件选择框,影刀的元素…

作者头像 李华
网站建设 2026/7/18 4:12:35

Android应用隐私合规检测与SDK权限监控实践

1. 隐私合规检测的必要性与挑战在移动应用生态中,第三方SDK已成为功能扩展的标配组件。根据行业调研数据,平均每个Android应用集成8.3个SDK,其中约37%的SDK会申请超出其功能所需的权限。这种过度索权行为不仅违反《个人信息保护法》的最小必要…

作者头像 李华
网站建设 2026/7/18 4:12:34

串口扫描技术全解析:从基础参数到工业应用

1. 串口扫描的核心需求与场景当我们需要与嵌入式设备、工业控制器或老旧外设通信时,串口往往是唯一可用的接口。但面对一台新设备时,首先遇到的难题就是:这个串口究竟支持哪些参数?它的硬件特性是什么?如何确认它确实可…

作者头像 李华