HarmonyOS 应用开发《掌上英语》第23篇-图标与颜色系统构建可主题化视觉体系

图标与颜色系统------构建可主题化的视觉体系

一、引言

在大型应用中,一致且可主题化的视觉体系是保证产品品质的基础。颜色和图标作为最基础的视觉元素,如果随意使用散落的色值和图标引用,将导致维护成本急剧上升,主题切换更无从谈起。

HarmonyOS 提供了 $r() 资源引用语法和 color.json / float.json 等资源文件,让我们能够以工程化的方式管理视觉体系。本文从颜色系统、图标选择和暗色模式三个维度,结合英语学习 App 的实践,详细讲解如何构建可主题化的视觉体系。

二、颜色系统:从散落色值到集中管理

2.1 资源文件结构

HarmonyOS 使用 color.json 文件集中管理颜色变量。同时,系统预置了丰富的 sys.color.* 资源可供直接使用:

复制代码
resources/
├── base/
│   └── element/
│       ├── color.json       # 基础颜色定义
│       └── float.json       # 间距、字号等浮点数值
├── dark/
│   └── element/
│       └── color.json       # 暗色模式颜色覆盖

2.2 系统级颜色资源

项目中大量使用系统预置的 sys.color.* 资源,这些资源会根据系统主题(浅色/深色)自动切换:

typescript 复制代码
// 字体颜色层级------系统资源
.fontColor($r('sys.color.font_primary'))       // 主要文字(浅色模式为 #000000,深色模式为 #FFFFFF)
.fontColor($r('sys.color.font_secondary'))      // 次要文字(用于副文本)
.fontColor($r('sys.color.font_tertiary'))       // 三级文字(用于提示文本)

// 背景颜色层级------系统资源
.backgroundColor($r('sys.color.background_primary'))    // 白色背景
.backgroundColor($r('sys.color.background_secondary'))  // 浅灰背景
.backgroundColor($r('sys.color.comp_background_tertiary')) // 三级容器背景

字体颜色层级的最佳实践

层级 资源名 使用场景 示例
一级 font_primary 标题、正文核心内容 单词名、课程名称
二级 font_secondary 副标题、辅助信息 音标、学习进度
三级 font_tertiary 提示、占位符 "点击卡片翻转"

2.3 应用级颜色定义

除了系统资源,项目在不同模块的 color.json 中定义了业务相关的颜色:

json 复制代码
// AppScope/resources/base/element/color.json
{
  "color": [
    {
      "name": "system_color_background_white",
      "value": "#ffffff"
    },
    {
      "name": "system_color_background_gray",
      "value": "#F1F3F5"
    }
  ]
}

// product/entry/src/main/resources/base/element/color.json
{
  "color": [
    {
      "name": "start_window_background",
      "value": "#FFFFFF"
    },
    {
      "name": "icon_bg_blue",
      "value": "#4B5CC4"
    }
  ]
}

// features/minePage/src/main/resources/base/element/color.json
{
  "color": [
    {
      "name": "time_btn",
      "value": "#4B5CC4"
    }
  ]
}

2.4 主色与业务色彩

项目中直接使用的特定色值(非 sys.color 资源)主要分为几类:

主题蓝色系(主品牌色):

typescript 复制代码
// #165DFF ------ 主品牌色,用于标题和关键操作
Text(this.currentWord().word)
  .fontSize(34).fontWeight(FontWeight.Bold)
  .fontColor('#165DFF');  // 单词使用主题蓝突出显示

Button('右滑 已掌握')
  .backgroundColor('#165DFF');  // 按钮主色

// #EAF2FF ------ 蓝色浅背景,用于标签/徽章
Text(this.currentWord().partOfSpeech)
  .fontSize(12).fontColor('#165DFF')
  .backgroundColor('#EAF2FF')  // 浅蓝色背景标签
  .borderRadius(12);

功能色彩

typescript 复制代码
const COLORS = {
  favorite: '#FFB020',    // 收藏金黄色 ------ ★ 收藏星标
  success: '#64BB5C',     // 绿色 ------ 答题正确
  error: '#E84026',       // 红色 ------ 答题错误
  primary: '#4B5CC4',     // 蓝紫色 ------ icon 背景、特殊按钮
  warning: '#FFc000',     // 橙黄 ------ 待复习按钮
};

2.5 颜色命名规范

定义应用级颜色时,建议遵循以下命名约定:

命名模式 示例 说明
{module}_{purpose} time_btn 模块前缀 + 用途
{area}_{role} topic_page_background_gray 区域 + 角色
{component}_{state} icon_bg_blue 组件 + 状态

三、图标系统:SVG vs PNG 的选择策略

3.1 统一的图标引用方式

在 HarmonyOS 中,图标通过 $r('app.media.*') 引用媒体资源:

typescript 复制代码
// 首页核心功能图标
new CourseCoreBar($r('app.media.ic_search'), '搜单词')
new CourseCoreBar($r('app.media.ic_practice_camera'), '搜单词')
new CourseCoreBar($r('app.media.ic__practice_wrong_question'), '易错词')
new CourseCoreBar($r('app.media.ic_practice_star'), '生词本')
new CourseCoreBar($r('app.media.ic_practice_notes'), '笔记')
new CourseCoreBar($r('app.media.ic_chart_report'), '学习报告')

// 练习模式图标
new PracticeView($r('app.media.ic_sequence'), '单词记忆', '每日单词打卡')
new PracticeView($r('app.media.ic_practice_simulations'), '听力训练', '沉浸式听力练习')
new PracticeView($r('app.media.ic_practice_test_paper'), '阅读训练', '英文原著阅读')
new PracticeView($r('app.media.ic_wrong_question'), '语法练习', '语法专项突破')

3.2 SVG 与 PNG 的选取标准

维度 SVG PNG
缩放质量 无损,任意尺寸清晰 有损,放大有锯齿
文件大小 较小(复杂图形除外) 取决于分辨率
支持颜色修改 可通过 CSS 改变 不能直接修改
兼容性 部分旧设备可能不支持 全兼容
适用场景 简单图标、Logo 复杂插图、照片

项目实践建议

  1. 功能图标使用 SVG (如 ic_searchic_practice_star):尺寸小、颜色统一、支持主题色
  2. Banner 使用 PNG/JPG (如 banner1):包含渐变和复杂视觉效果,SVG 难以表现
  3. 准备双份资源 :在 media/ 目录存放 SVG/PNG,在 media/ 提供同名的 PNG 备选

3.3 图标尺寸规范

项目中图标尺寸的引用分两种方式:

固定尺寸(直接写数值):

typescript 复制代码
// 小图标 24×24
Image($r('app.media.ic_search')).width(28).height(28);

// 中图标 32×32
Image(this.item.imageSrc).width('32vp').height('32vp');

// 大图标 40×40
Image(this.item.imageUri).width(40).height(40);

资源变量引用(使用 float.json 中的变量):

typescript 复制代码
// 通过 $r('app.float.*') 引用
.width($r('app.float.QuestionModel_ICON_SIZE'))
.height($r('app.float.QuestionModel_ICON_SIZE'))

// 间距也通过 float 资源管理
.padding($r('app.float.vp_12'))
.margin($r('app.float.vp_16'))

使用 app.float 资源管理间距和尺寸的好处是:当需要全局调整间距时,只需修改一处 float.json 即可生效。

四、暗色模式:自动与手动切换

4.1 颜色资源覆盖机制

HarmonyOS 的暗色模式通过 dark/element/color.json 目录实现。当系统切换到深色模式时,同名颜色会自动覆盖:

json 复制代码
// product/entry/src/main/resources/base/element/color.json(浅色)
{
  "color": [
    {
      "name": "start_window_background",
      "value": "#FFFFFF"
    }
  ]
}

// product/entry/src/main/resources/dark/element/color.json(深色)
{
  "color": [
    {
      "name": "start_window_background",
      "value": "#000000"
    }
  ]
}

// components/login_info/dark/element/color.json
{
  "color": [
    {
      "name": "login_page_bg",
      "value": "#202224"      // 深色模式登录页背景
    }
  ]
}

// components/answer_questions/dark/element/color.json
{
  "color": [
    {
      "name": "answer_sheet_bg",
      "value": "#202224"      // 深色模式答题卡背景
    }
  ]
}

4.2 sys.color 的自动适配

使用 $r('sys.color.*') 引用系统颜色资源时,暗色模式适配由系统自动完成:

typescript 复制代码
// 无需任何修改,自动适配暗色模式
Text('Hello')
  .fontColor($r('sys.color.font_primary'))  // 浅色=#000000, 深色=#FFFFFF
backgroundColor($r('sys.color.background_primary'))  // 浅色=#FFFFFF, 深色=#1C1C1E

使用 sys.color 的三个原则

  1. 文字颜色 :始终使用 font_primary/secondary/tertiary,不用硬编码
  2. 背景颜色 :始终使用 background_primary/secondary,不用硬编码
  3. 分隔线/边框 :使用 comp_divider 等系统资源

4.3 主题切换的手动控制

项目提供了"跟随系统 / 普通模式 / 夜间模式"三种选项,通过 ConfigurationConstant 实现:

typescript 复制代码
// features/minePage/src/main/ets/views/SetupPage.ets
private switchColorMode(item: string): void {
  let colorMode: number;
  if (item === '跟随系统') {
    colorMode = ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET;  // -1
  } else if (item === '普通模式') {
    colorMode = ConfigurationConstant.ColorMode.COLOR_MODE_LIGHT;    // 1
  } else {
    colorMode = ConfigurationConstant.ColorMode.COLOR_MODE_DARK;     // 0
  }
  this.getUIContext()?.getHostContext()?.getApplicationContext()
    .setColorMode(colorMode);
  PreferenceUtil.getInstance().put(PreferConstant.COLOR_MODE, colorMode);
}

实现要点

  • COLOR_MODE_NOT_SET(-1):跟随系统,这是推荐默认值
  • COLOR_MODE_LIGHT(1):强制浅色模式
  • COLOR_MODE_DARK(0):强制深色模式
  • 设置后需要持久化到 Preferences,确保下次启动时恢复用户选择

4.4 sys 资源 vs app 资源使用场景

资源引用 适用场景 示例
$r('sys.color.font_primary') 文字颜色 标题、正文
$r('sys.color.background_primary') 页面/卡片背景 内容区、卡片
$r('sys.float.Body_S') 系统字号 正文文字
$r('sys.float.Caption_M') 辅助字号 说明文字
$r('app.color.system_color_background_white') 应用自定义颜色 业务专用色
$r('app.float.vp_12') 应用间距 padding、margin

五、最佳实践总结

5.1 编码规范

  1. 绝不硬编码颜色值 :除主题品牌色(#165DFF)外,全部使用 $r() 资源引用
  2. 主题色使用 sys.color:优先使用系统资源,确保自动适配暗色模式
  3. 业务色定义在 color.json:应用级颜色按模块分文件管理
  4. 图标统一 $r('app.media.*'):不使用网络图片图标

5.2 颜色检查清单

  • 所有文字颜色使用 sys.color.font_* 层级
  • 卡片/页面背景使用 sys.color.background_*
  • 业务特定色值在 color.json 中定义并引用
  • 暗色模式资源在 dark/element/color.json 中覆盖
  • 主题切换逻辑持久化用户偏好

六、总结

可主题化的视觉体系是现代应用开发的基石。通过 color.json 集中管理颜色变量、$r() 统一引用资源、dark/ 目录提供暗色覆盖,我们构建了一个支持浅色/深色无缝切换的视觉体系。图标方面,根据场景选择 SVG 或 PNG,配合 app.media 资源引用,保证了图标的统一管理。

颜色和图标的管理看似是"小事",但正是这些细节决定了应用的主题化能力和后期维护成本。合理的资源规划,让英语学习 App 在功能迭代的同时,始终保持着一致的视觉品质。

相关推荐
红烧大青虫6 小时前
HarmonyOS应用开发实战:小事记 - UIAbility 的冷启动/热启动/后台启动三种场景与 launchParam 解析
后端·华为·harmonyos·鸿蒙系统
b130538100497 小时前
HarmonyOS应用开发实战:小事记 - 多级页面路由的 back 逻辑与参数回传模式
华为·harmonyos·鸿蒙系统
MonkeyKing7 小时前
鸿蒙ArkTS Text组件全解析:全属性详解+富文本实战
harmonyos
花开彼岸天~7 小时前
鸿蒙原生开发手记:徒步迹 - 自定义组件开发规范
后端·华为·harmonyos·鸿蒙系统
zSD55rt5a8 小时前
方差在扩散模型保护中的作用
人工智能·harmonyos
AD02278 小时前
HarmonyOS应用实战-启示散页-05-随机抽答案要避免连续重复:把算法放进服务层
harmonyos·arkts·鸿蒙开发
世人万千丶9 小时前
鸿蒙Flutter Flexible与Expanded的区别
学习·flutter·harmonyos·鸿蒙
红烧大青虫9 小时前
setInterval 倒计时实现:60s 验证码发送逻辑
后端·华为·harmonyos·鸿蒙系统
程序员黑豆9 小时前
鸿蒙应用开发中的单位详解:px、vp、fp、lpx
前端·harmonyos