news 2026/8/27 13:09:31

【中国方言题库|17】HarmonyOS ArkTS 亮暗色与视觉令牌实战:集中颜色、间距和交互状态避免页面割裂

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【中国方言题库|17】HarmonyOS ArkTS 亮暗色与视觉令牌实战:集中颜色、间距和交互状态避免页面割裂

主题适配常被简化成“准备一套浅色和一套深色颜色”,但真正影响工程质量的,是页面有没有统一使用语义颜色、字号、间距、圆角和交互状态。只要一部分页面直接写十六进制颜色,另一部分引用常量;一个模块把正文灰定义为#444444,另一个模块定义为#3D4A45;资源目录虽然有dark,应用启动却强制锁定浅色,那么“支持深色模式”就只是文件结构上的表象。

中国方言题库的真实 ArkTS 源码提供了一个很典型的阶段性方案:公共组件和业务页面大量引用ColorsSizes,形成墨绿、米白、白色卡片的统一视觉语言;答题选项的默认、选中、正确、错误状态都有独立语义令牌;输入框还显式定义文字、占位符和背景色,避免系统颜色变化造成不可读。与此同时,EntryAbility明确调用COLOR_MODE_LIGHT,当前版本选择锁定浅色;basedark资源文件内容相同,页面主令牌又是硬编码字符串,所以不能宣称现有版本已经完成亮暗色自动切换。

本文面向 HarmonyOS 5.0 及以上版本,围绕真实源码解释视觉令牌怎样减少页面割裂、当前浅色锁定为什么能保持一致、若未来跟随系统深色模式需要改造哪些层。本文唯一核验标记:视觉令牌统一语义,颜色模式决定令牌如何解析

一、先核对真正生效的主题文件

本文复核的主要文件是:

entry/src/main/ets/common/constants/ThemeConstants.ets librarya/src/main/ets/constants/ThemeConstants.ets entry/src/main/ets/entryability/EntryAbility.ets entry/src/main/resources/base/element/color.json entry/src/main/resources/dark/element/color.json entry/src/main/ets/common/components/GreenButton.ets entry/src/main/ets/common/components/BankCard.ets entry/src/main/ets/pages/PracticePage.ets entry/src/main/ets/pages/SettingsPage.ets entry/src/main/ets/views/HomePage.ets entry/src/main/ets/views/FavoritePage.ets

队列 brief 指向了entry/src/main/ets/common/constants/ThemeConstants.ets,该文件确实存在;工程同时还有librarya的公共主题常量。多数页面通过:

import { Colors, Sizes } from 'librarya'

使用公共能力层令牌,少量与资源图标、等级印章绑定的映射仍位于 entry。分析主题时不能只看一个同名文件,否则会遗漏实际导入来源。

二、当前版本究竟支不支持深色模式

先给出结论:当前应用正常启动后锁定浅色模式,并没有跟随系统切换到深色模式。

证据位于EntryAbility.onCreate()

this.context .getApplicationContext() .setColorMode( ConfigurationConstant.ColorMode.COLOR_MODE_LIGHT )

调用被放在try/catch中,失败时记录日志。只要设置成功,应用就会使用浅色模式,不会因为系统切到深色而自动改变。

工程确实存在:

entry/src/main/resources/base/element/color.json entry/src/main/resources/dark/element/color.json

但两个文件当前颜色值相同,例如背景、表面和文本都还是浅色值。这说明dark目录更像占位或防御性资源,并不是一套已完成的深色设计。

更关键的是,页面主要使用的Colors.PRIMARYColors.BACKGROUND等是 ArkTS 字符串常量:

static readonly BACKGROUND: string = '#F5F1E8' static readonly SURFACE: string = '#FFFFFF' static readonly TEXT_PRIMARY: string = '#1F2A26'

它们不会因为资源限定目录变化而自动切换。即便移除浅色锁定,仅修改dark/element/color.json,这些硬编码令牌仍保持浅色。当前版本的正确描述是“视觉令牌集中管理,并有明确的浅色锁定策略”,而不是“已经支持亮暗色自适应”。

三、为什么锁定浅色也必须认真设计

锁定浅色并不等于可以忽略系统主题。用户可能在深色系统环境中启动应用,启动窗口、状态栏、输入控件、弹窗和第三方组件都有可能受系统默认样式影响。如果页面背景是米白色,而某个输入框文字沿用深色模式下的系统默认浅色,就可能出现白底白字。

libraryaColors为输入组件增加了:

static readonly INPUT_TEXT: string = '#1F2A26' static readonly INPUT_PLACEHOLDER: string = '#6F7873' static readonly INPUT_BG: string = '#FFFFFF'

注释明确说明INPUT_TEXT用于显式覆盖系统深色默认色。这个处理与应用锁浅色的策略一致:既然产品决定保持浅色,就必须让容易受系统默认主题影响的组件也显式使用浅色令牌。

锁定策略还应覆盖启动窗口。当前 base 和 dark 的start_window_background都是#FFFFFF,避免系统深色时出现黑色启动窗口、进入应用后突然切到米白背景的闪变。它不是完整深色适配,却是保持浅色视觉连续性的必要措施。

四、Colors 的职责不是保存颜色名,而是表达语义

公共主题类没有按“绿色一、绿色二、灰色一”命名,而是分为主色、背景、文本、状态和答题选项:

class Colors { static readonly PRIMARY = '#2F6B5E' static readonly BACKGROUND = '#F5F1E8' static readonly SURFACE = '#FFFFFF' static readonly TEXT_PRIMARY = '#1F2A26' static readonly TEXT_SECONDARY = '#3D4A45' static readonly TEXT_HINT = '#5F6763' static readonly SUCCESS = '#267A55' static readonly ERROR = '#B3261E' }

页面关心的是“这是页面背景”“这是主标题”“这是错误状态”,而不是某个具体色值。未来品牌色调整时,只需修改令牌定义,使用Colors.PRIMARY的按钮、标签、进度和选中态会一起变化。

语义化还能减少误用。例如“清除所有学习数据”使用Colors.ERROR,正确答案使用Colors.SUCCESS,普通辅助文案使用Colors.TEXT_HINT。如果所有页面只拿一个REDGRAY,开发者很难判断它该用于错误、危险操作、装饰还是禁用状态。

五、背景层级如何避免页面像拼起来的

中国方言题库使用三个主要背景层:

BACKGROUND -> 页面底色,米白 #F5F1E8 SURFACE -> 卡片、按钮底、底部操作区,白色 #FFFFFF BACKGROUND_ALT -> 卡片内部说明区,浅米白 #FBF8F0

题库详情页根容器使用Colors.BACKGROUND,学习摘要、简介、重点和章节卡片使用Colors.SURFACE,简介卡片内部的文化提示使用Colors.BACKGROUND_ALT。三层关系稳定后,页面之间不会出现一个用纯灰底、一个用米白底、一个又用未经约束的透明底。

设置页也遵循相同模式:页面根背景是BACKGROUND,分组卡片是SURFACE,选择器未选项是BACKGROUND_ALT。即使组件结构不同,用户仍能识别“页面、卡片、内嵌选项”的视觉层级。

六、Sizes 如何统一信息密度

颜色只是视觉令牌的一部分。Sizes集中定义:

static readonly H1_FONT: number = 22 static readonly H2_FONT: number = 18 static readonly TITLE_FONT: number = 16 static readonly BODY_FONT: number = 14 static readonly CAPTION_FONT: number = 12 static readonly SMALL_FONT: number = 10 static readonly CARD_RADIUS: number = 16 static readonly CARD_RADIUS_SM: number = 10 static readonly BTN_RADIUS: number = 24 static readonly PADDING_SMALL: number = 8 static readonly PADDING_MEDIUM: number = 12 static readonly PADDING_LARGE: number = 16 static readonly PADDING_XL: number = 20

因此首页标题、页面二级标题、卡片标题、正文和辅助文字有清晰层级;常规卡片圆角和小组件圆角也能保持一致。开发页面时不需要反复决定“这里到底是 15 还是 16”“这个卡片圆角是 14 还是 18”。

但源码中仍有一些局部硬编码,例如首页品牌标题使用 25,考试页标题使用 24,部分卡片使用圆角 18 或 22。这些值可能出于视觉强调,并不自动构成错误;真正的问题是它们没有语义名称,后续统一调整时难以检索意图。建议把稳定复用的特殊规格补成PAGE_TITLE_FONTHERO_RADIUS等令牌,单次特例则保留在组件内部。

七、交互状态为什么需要成组定义

答题选项不是只有“选中”和“没选中”。公共令牌为它定义了完整状态:

OPTION_BG OPTION_BORDER OPTION_SELECTED_BG OPTION_SELECTED_BORDER OPTION_CORRECT_BG OPTION_CORRECT_BORDER OPTION_WRONG_BG OPTION_WRONG_BORDER

这组设计把填充和边框成对组织。例如正确选项使用浅绿色背景配成功绿色边框,错误选项使用浅红背景配错误红边框。页面不必临时拼接颜色,也不容易出现“背景表达正确,边框却仍是选中主色”的冲突。

交互状态还需要文字和图标配合。只改变背景色对色觉差异用户不够友好,题目页面还应通过正确/错误文案、图标或结果说明传达状态。当前令牌层解决的是颜色一致性,不等于已经替代完整的无障碍反馈。

八、GreenButton 如何复用主操作语言

GreenButton支持实心和描边两种形态。实心按钮使用:

.fontColor(Color.White) .linearGradient({ angle: 180, colors: [ [Colors.PRIMARY, 0], [Colors.PRIMARY_DARK, 1] ] })

描边按钮使用:

.fontColor(Colors.PRIMARY) .borderColor(Colors.PRIMARY) .backgroundColor(Colors.SURFACE)

两个按钮共享Sizes.BODY_FONT,圆角由高度的一半计算,形成胶囊形操作按钮。题库详情页的“随机练习”和“模拟考试”、结果页的“错题解析”和“再考一次”都可使用同一组件,主次关系保持一致。

当前组件没有显式的 pressed、disabled、loading 参数。PRIMARY_DARK注释提到可用于按钮按压或渐变下端,但现有GreenButton只把它用于渐变。若后续增加提交、保存或耗时操作,应扩展状态接口,而不是在各页面外层临时改透明度。

九、选中态如何避免只靠一处变化

设置页底部选择器的已选项同时改变多项属性:

.fontColor(selected ? Colors.PRIMARY : Colors.TEXT_PRIMARY) .fontWeight(selected ? FontWeight.Bold : FontWeight.Normal) .backgroundColor( selected ? Colors.PRIMARY_LIGHT : Colors.BACKGROUND_ALT )

并额外显示“已选”标签:

Text('已选') .fontColor(Color.White) .backgroundColor(Colors.PRIMARY)

这比只改变文字颜色更可靠。用户可以从字重、背景和标签三个维度识别状态。收藏页 Tab 也会在选中时使用主色背景和白色文字,未选中时使用表面色与次要文字。

不过,点击、按压和键盘焦点状态在源码中没有形成统一令牌。对于手机触控,选中态比较完整;若面向 2in1 鼠标键盘,还需要补充 hover、focus 和 pressed 的视觉反馈。

十、alpha() 解决的是透明度复用,不是主题切换

两个主题文件都提供:

export function alpha(color: string, alphaHex: string): string { if (!color || color.length < 7) return color return '#' + alphaHex + color.substring(1, 7) }

ArkUI 使用#AARRGGBB,该函数把透明度放在 RGB 前面。例如:

alpha('#2F6B5E', '15')

得到#152F6B5E。它适合生成主色的低透明背景或阴影,使同一品牌色在不同层级复用。

函数假设传入的是至少七位的#RRGGBB字符串,没有验证alphaHex是否为两位合法十六进制,也不会正确处理资源对象或完整八位色。当前调用范围简单时足够;如果主题系统扩展,最好把输入约束写成更明确的接口,避免把动态主题资源强行当字符串处理。

十一、两个 ThemeConstants 为什么是潜在漂移点

entry 与 librarya 都定义了ColorsSizes和部分题型/等级映射。它们看起来接近,但并不完全相同:

entry TEXT_SECONDARY = #444444 librarya TEXT_SECONDARY = #3D4A45 librarya 额外包含 INPUT_TEXT / INPUT_PLACEHOLDER / INPUT_BG librarya 额外包含 TOUCH_TARGET entry 的 RankInfo 包含 stamp Resource librarya 的 RankInfo 只有 label 和 color

这说明两份文件已经承担不同职责:librarya 是公共视觉令牌,entry 还绑定了 app 资源。但重复定义同名ColorsSizes会带来漂移风险。某个页面如果误从 entry 导入,正文色就可能与从 librarya 导入的组件不同。

更稳的边界是:

librarya -> Colors、Sizes、纯数据映射 entry -> 资源图标、印章 Resource、应用专属映射 -> 直接复用 librarya 的 Colors

这样颜色和尺寸只有一个真源,entry 不再复制数值。

十二、资源颜色与 ArkTS 字符串令牌目前没有打通

color.json中定义了:

{ "name": "text_primary", "value": "#212121" }

Colors.TEXT_PRIMARY是:

static readonly TEXT_PRIMARY: string = '#1F2A26'

两者色值不同,页面又主要使用后者。也就是说,资源文件与 ArkTS 令牌是两套并行系统,修改资源文件不会自动影响这些页面。

这不是文章推测,而是由引用方式直接决定的:只有$r('app.color.text_primary')才会按资源限定目录解析;普通字符串'#1F2A26'不会访问资源系统。

如果产品继续锁浅色,保留字符串令牌也能工作,但应减少无效或过时的资源定义,避免维护者误以为修改color.json已改变全局主题。如果计划支持跟随系统,则应让语义令牌最终指向资源。

十三、迁移到真正亮暗色适配的第一步

未来若要跟随系统,第一步不是删除COLOR_MODE_LIGHT,而是先建立资源语义一致性。

可在 base 中定义:

{ "color": [ { "name": "app_background", "value": "#F5F1E8" }, { "name": "app_surface", "value": "#FFFFFF" }, { "name": "text_primary", "value": "#1F2A26" }, { "name": "text_secondary", "value": "#3D4A45" }, { "name": "text_hint", "value": "#5F6763" } ] }

dark 中使用同名资源:

{ "color": [ { "name": "app_background", "value": "#111614" }, { "name": "app_surface", "value": "#1B211F" }, { "name": "text_primary", "value": "#F2F5F3" }, { "name": "text_secondary", "value": "#CDD5D1" }, { "name": "text_hint", "value": "#AEB8B3" } ] }

重点不是示例色值本身,而是两套资源名称必须一致。页面只引用语义名,系统根据当前颜色模式解析对应值。

十四、第二步:让组件消费资源,而不是静态十六进制字符串

页面可逐步从:

.fontColor(Colors.TEXT_PRIMARY) .backgroundColor(Colors.BACKGROUND)

迁移为:

.fontColor($r('app.color.text_primary')) .backgroundColor($r('app.color.app_background'))

也可以建立一个能返回ResourceColor的主题令牌层,统一封装$r引用。这里要注意类型设计:现有Colors字段是string,而资源引用属于Resource/ResourceColor使用场景。迁移不能只把字符串替换成$r而忽略接口类型。

应优先迁移根背景、表面、主要文本、次要文本、分隔线和输入控件,因为它们决定全页可读性;品牌色和状态色可在确认两种模式对比度后再迁移。

十五、第三步:处理图片、阴影和覆盖层

颜色资源切换后,仍有几类视觉元素不会自动安全:

  1. 题库封面图片可能在深色背景中过亮。
  2. 深色卡片上的CARD_SHADOW可能几乎不可见。
  3. #66000000蒙层在深色背景上层次不足。
  4. 白色文字与渐变主色需要重新检查对比度。
  5. 启动窗口、状态栏和导航栏图标颜色必须与背景一致。

这些元素应在真机或模拟器中逐页验证,不能只靠资源 JSON 静态检查。

十六、对比度不是“看着清楚”就算通过

当前令牌注释中,TEXT_HINT标记为符合 4.5:1,正文色也做了对比度增强。工程审查应按实际前景/背景组合计算,而不是单独评价某个颜色。

需要覆盖:

TEXT_PRIMARY / BACKGROUND TEXT_SECONDARY / SURFACE TEXT_HINT / BACKGROUND PRIMARY / PRIMARY_LIGHT White / PRIMARY ERROR / SURFACE SUCCESS / OPTION_CORRECT_BG INPUT_PLACEHOLDER / INPUT_BG 禁用态与半透明态

正文通常应达到 4.5:1,关键图标、按钮文字等至少应保持清晰可辨。深色模式不能简单把背景换黑、文字换白,因为状态色在深色底上的亮度关系也会变化。

十七、视觉令牌怎样减少跨页面割裂

以首页、题库详情、收藏页和设置页为例,它们的业务完全不同,但共同使用:

BACKGROUND 作为根背景 SURFACE 作为卡片背景 TEXT_PRIMARY 作为标题 TEXT_HINT 作为辅助信息 PRIMARY 作为主要动作或选中状态 DIVIDER 作为列表分隔 CARD_RADIUS 作为常规卡片圆角 PADDING_LARGE 作为页面水平间距

因此用户从首页进入详情,再进入设置,视觉层级仍是连续的。令牌系统的价值不只是“改色方便”,而是把设计决定变成代码契约:同一语义在不同页面必须有相同表现。

反过来,页面里仍存在Color.White#E6FFFFFF#66000000等直接值。白色用于主色渐变上的文字具有明确局部语义,半透明蒙层也可能是组件特例,并非所有硬编码都要消灭。应优先清理重复、跨页面使用且可能随主题变化的值。

十八、主题状态不应和业务状态混在一起

如果未来增加“跟随系统 / 浅色 / 深色”设置,不建议把主题切换逻辑散落在页面。设置页只负责选择,服务层负责保存偏好,应用或主题控制器负责调用颜色模式 API。

建议链路是:

设置页选择主题 -> ThemePreferenceService 保存模式 -> ThemeController 应用模式 -> 资源系统重新解析同名颜色 -> 页面自动刷新

主题偏好是轻量设置,可使用 Preferences;页面不应在每次 build 时重复调用setColorMode。颜色模式 API 与 Ability 生命周期相关,应由应用级边界统一管理。

十九、当前版本适合执行的验证矩阵

由于当前策略是锁定浅色,测试重点不是“深色页面是否漂亮”,而是“系统深色环境下浅色应用是否仍稳定”:

系统浅色 + 应用启动 系统深色 + 应用启动 运行中切换系统颜色模式 冷启动窗口到首页的背景连续性 输入框文字与占位符 状态栏和导航栏图标 设置页底部选择器 题目正确、错误、选中状态 清除数据危险操作 弹层蒙版和取消动作

系统切换后,应用应继续保持浅色,不出现某些系统控件突然变暗、文字反色或启动页闪黑。若setColorMode调用失败,日志会记录错误,此时也需要观察回退表现。

二十、未来跟随系统时的验收矩阵

移除浅色锁定并完成资源迁移后,再扩展为:

浅色:所有主页面与二级页 深色:所有主页面与二级页 运行中浅转深 运行中深转浅 冷启动恢复用户偏好 跟随系统模式 输入、弹窗、选择器、列表、卡片 正确、错误、警告、选中、禁用、按压 状态栏、导航栏、启动窗口 图片、图标、阴影、蒙层

特别要检查设置页、练习页和考试结果页,因为它们包含状态色、底部操作区和多层表面,最容易出现局部遗漏。

二十一、最小改造顺序

建议按以下顺序推进,避免一次性改完后难以定位问题:

1. 确认产品策略:继续锁浅色,还是跟随系统 2. 合并 entry 与 librarya 的重复 Colors / Sizes 3. 清理 base 与 dark 资源的同名同值占位 4. 将背景、表面、文本、分隔线迁移为资源令牌 5. 迁移输入框和弹层 6. 迁移状态色与答题选项 7. 增加主题设置与持久化 8. 验证状态栏、导航栏和启动窗口 9. 执行全页面对比度与截图回归

如果产品决定继续锁浅色,那么第 2、3 步仍然值得做:统一令牌真源,明确 dark 资源只是浅色锁定下的防御性镜像,并在项目文档中记录策略。

二十二、结语

中国方言题库当前最真实的主题能力,是一套覆盖颜色、字号、间距、圆角、输入和答题状态的视觉令牌,以及明确的浅色锁定策略。它已经有效减少首页、题库、练习、收藏和设置之间的视觉割裂,但还不是完整的亮暗色自动适配:EntryAbility锁定浅色,dark 资源与 base 相同,主要页面又使用静态字符串色值。

工程上最重要的判断是:视觉令牌统一语义,颜色模式决定令牌如何解析。先把同一语义收敛到唯一真源,再决定锁浅色还是跟随系统;如果选择深色适配,就让令牌真正连接资源系统,并验证输入、状态、系统栏和启动窗口。这样主题能力才不是一组散落色值,而是一条可维护、可切换、可复核的视觉契约。

AI 辅助声明:本文在结构整理和语言润色环节使用了 AI 辅助;关于浅色锁定、资源文件、颜色值、令牌重复和组件状态的描述均依据中国方言题库真实 ArkTS 与资源文件复核。

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

SAP IDoc出站与入站配置

一、发送端配置出站IDoc1、WE31 创建段类型&#xff0c;此处我用标准段类型 E1P0000 演示2、WE30 创建IDoc类型&#xff0c;此处我用标准IDoc类型 HRMD_A09 演示3、WE81 创建消息类型4、WE82 关联消息类型与IDoc类型5、WE21 创建接收端的端口&#xff0c;其中RFC目标为SM59创建…

作者头像 李华
网站建设 2026/8/27 13:06:34

HarmonyOS DevEco Studio 3.1 Beta新特性知多少

HUAWEI DevEco Studio是开发HarmonyOS应用及服务的一站式集成开发环境&#xff08;IDE&#xff09;。本次&#xff0c;DevEco Studio 3.1 Beta新增支持当前在市面上新的操作系统&#xff0c;如Windows11、macOS&#xff08;ARM&#xff09;操作系统&#xff0c;还面向HarmonyOS…

作者头像 李华
网站建设 2026/8/27 13:06:03

鸿蒙生态:下一个移动互联网的黄金领域,你敢来挑战吗?

要说2023年IT圈最火爆的名词&#xff0c;非鸿蒙莫属&#xff01;2023年9月25日&#xff0c;华为宣布将在2024年第一季度推出HarmonyOS NEXT版本&#xff0c;这一消息让鸿蒙原生应用开发摆脱了Android手机系统的束缚&#xff0c;引领鸿蒙应用开发迈向新的纪元。 随着这一消息的发…

作者头像 李华
网站建设 2026/8/27 13:01:07

鸿蒙开发入门:应用配置文件概述(FA模型)

应用配置文件概述&#xff08;FA模型&#xff09; 每个应用项目必须在项目的代码目录下加入配置文件&#xff0c;这些配置文件会向HarmonyOS的编译工具、HarmonyOS操作系统和应用市场提供描述应用的基本信息。 应用配置文件需申明以下内容&#xff1a; 应用的软件包名称&…

作者头像 李华
网站建设 2026/8/27 12:59:44

5 分钟用 Driver Store Explorer 清掉旧驱动,免费释放 C 盘空间

5 分钟用 Driver Store Explorer 清掉旧驱动&#xff0c;免费释放 C 盘空间 【免费下载链接】DriverStoreExplorer Driver Store Explorer 项目地址: https://gitcode.com/gh_mirrors/dr/DriverStoreExplorer 周五要交周报前 10 分钟&#xff0c;C 盘弹了个红条。你想清…

作者头像 李华
网站建设 2026/8/27 12:59:20

MD常见报错解决方法

一、残基不全问题该问题一般出现于使用电镜解析结构跑MD的时候&#xff0c;主要是因为有些质量密度分辨率不足&#xff0c;只能确定部分原子的位置&#xff0c;从而出现了少于标准残基原子数的残基&#xff0c;导致GROMACS无法识别。该问题从GROMACS的报错中可能无法判断出问题…

作者头像 李华