首页 > 数据治理 > 数据治理 > 技术解读:鸿蒙系统深色模式功能详解与开发适配指南
深色模式是一种满足用户个性化需求的界面显示模式,它基于人因研究设计了深色环境下舒适的颜色范围。当深色模式开启后,原本浅色主题的应用界面背景会变为深色,而文字、图标等前景元素则变为浅色。
内容更加突出 :深色背景使前景内容(文字、图标)更加醒目,提升信息识别效率。
OLED屏幕省电 :在OLED屏幕设备上,深色模式下屏幕总发光量更少,从而降低功耗,延长续航。
视觉舒适与沉浸式体验 :尤其在暗光环境下,深色模式能减少刺眼感,提供更舒适的观看体验,同时营造沉浸感。
由于部分用户倾向于在暗环境使用深色模式,也有相当比例的用户出于对深色风格的喜爱或节能省电需求(OLED屏幕功耗更低)而全天开启深色模式。因此,深色模式设计需要兼顾全天候清晰易读、暗环境使用舒适性,以及与浅色模式在层级感知、色彩语义方面的一致性。
深色模式下,文本、图标等前景与深色背景之间必须满足最小对比度要求。根据WCAG(Web Content Accessibility Guidelines)要求及华为人因团队研究结果,对比度分级标准如下:
| 场景 | 推荐(优秀) | 一般 | 不建议 | | --- | --- | --- | --- | | 大字号(17fp或15fp以上粗体)、辅助文本(如列表二级文本,或其他对识别效率要求不高的场景)、功能性图标 | 大于3:1 | 1.9:1 – 3:1 | 小于1.9:1 | | 非大字号主要文本(如长文本正文、列表一级文本) | 浅色模式大于4.5:1;深色模式大于5:1 | 浅色模式3:1-4.5:1;深色模式3:1-5:1 | 小于3:1 | | 建议表示活动状态的可交互控件 | 控件背板与背景之间对比度不小于2.2:1 | \ | \ |
注 :装饰性文本、表示disable状态的文本、logo等特殊场景不需要满足以上要求。
此外,当同时使用多个多彩色表示不同状态或用于相邻位置时,除了满足对比度要求,还需满足色彩差异要求:
一般要求 :两个需要区分的颜色或相邻颜色,色彩差异△Euv >= 20。
无障碍要求 :考虑色盲群体,使用色盲模拟器后,两个要区分的颜色或相邻颜色,色彩差异△Euv >= 20。
为避免暗环境下刺眼等舒适性问题,深色模式设计需满足以下要求:
| 要求 | 推荐(优秀) | 一般 | 不合格 | | --- | --- | --- | --- | | 文字对比度上限 | 文字需要谨慎使用大于17.6:1的高对比度 | 15.7:1-17.6:1 | 大于17.6:1 | | 小图片、图标背板 | 对比度建议不大于15.7:1 | \ | \ | | 浅色控件使用与适配 | 同时满足:深色模式下应用切换为深色背景;应用内避免黑白跳转的页面;页面上不建议使用大面积白色图片或背景 | 深色模式下应用切换为深色背景,但存在黑白跳转的页面或大面积白色图片 | 深色模式下应用仍为浅色界面 |
多数应用设计及用户默认使用浅色模式,深色模式设计需保持与浅色模式的一致性,主要包括:
层级的一致性 :浅色模式下明度不是唯一表达层级的视觉线索,而在深色模式尤其是黑色背景上,投影感知降低,通常使用明度表达层级。不同层级之间需要有一定的明度区分,并与浅色模式感知一致。
色彩语义的一致性 :表示警示、通话等具有语义信息的颜色需要保持一致,可以在保持色相一致的前提下对明度进行调整。
同类控件风格一致性 :同一应用或不同页面中的同类控件,视觉风格(颜色、形状等)保持一致。
当前系统存在深浅色两种显示模式,为了给用户更好的使用体验,应用应适配深浅色模式。从应用与系统配置关联的角度,适配可分为两种情况:
应用跟随系统的深浅色模式
应用主动设置深浅色模式
在 resources 目录下增加深色模式限定词目录(命名为 dark ),并新建 color.json 文件,用于配置深色模式下的颜色资源。详细参考资源分类与访问。
resources ├── base │ └── element │ └── color.json └── dark └── element └── color.json
开发者可在两个 color.json 中定义同名配色并赋予不同的色值。例如:
base/element/color.json :
{
"color"
:
[
{
"name"
:
"app_title_color"
,
"value"
:
"#000000"
}
]
}dark/element/color.json :
{
"color"
:
[
{
"name"
:
"app_title_color"
,
"value"
:
"#FFFFFF"
}
]
}开发者可直接使用系统预置资源(分层参数),同一资源ID在设备类型、深浅色等不同配置下有不同的取值。通过使用系统资源,不同开发者可以开发出具有相同视觉风格的应用,无需自定义两份颜色资源,深浅色模式下会自动切换。例如,调用系统资源中的文本主要配色:
Text ( '使用系统定义配色' ) . fontColor ( $r ( 'sys.color.ohos_id_color_text_primary' ) )
采用资源限定词目录方式,将深色模式下对应的同名图片放到 dark/media 目录下,通过 $r 方式加载图片资源的key值,系统深浅色切换时自动加载对应资源文件中的value值。
对于SVG格式的简单图标,可以使用 fillColor 属性配合系统资源改变图片绘制颜色,无需两套图片资源即可实现适配:
Image ( $r ( 'app.media.pic_svg' ) ) . width ( 50 ) . fillColor ( $r ( 'sys.color.ohos_id_color_text_primary' ) )
Web组件支持对前端页面进行深色模式配置,可参考Web组件深色模式进行相关配置。
自定义节点 BuilderNode 和 ComponentContent 需手动传递系统环境变化事件,触发节点的全量更新,详细参考 BuilderNode 系统环境变化更新 updateConfiguration 。
const
builderNodeMap
:
BuilderNode
<
[
Params
]
>
[
]
=
[
]
;
class
MyFrameCallback
extends
FrameCallback
{
onFrame
(
)
{
updateColorMode
(
)
;
}
}
function
updateColorMode
(
)
{
builderNodeMap
.
forEach
(
(
value
,
index
)
=>
{
value
.
updateConfiguration
(
)
;
}
)
}
aboutToAppear
(
)
:
void
{
this
.
getUIContext
(
)
?.
postFrameCallback
(
new
MyFrameCallback
(
)
)
;
}应用可以主动监听系统深浅色模式变化,进行其他类型的资源初始化等自定义逻辑。 注意 :应用使用 setColorMode 手动设置深浅色的情况下,将不会收到 onConfigurationUpdate 回调。除此之外,无论应用是否跟随系统深浅色模式变化,该监听方式均可生效。
a. 在 AbilityStage 的 onCreate() 生命周期中获取APP当前的颜色模式并保存到 AppStorage :
onCreate
(
)
:
void
{
AppStorage
.
setOrCreate
(
'currentColorMode'
,
this
.
context
.
config
.
colorMode
)
;
}b. 在 AbilityStage 的 onConfigurationUpdate() 生命周期中获取最新更新的颜色模式并刷新到 AppStorage :
onConfigurationUpdate
(
newConfig
:
Configuration
)
:
void
{
AppStorage
.
setOrCreate
(
'currentColorMode'
,
newConfig
.
colorMode
)
;
hilog
.
info
(
0x0000
,
'testTag'
,
'the newConfig.colorMode is %{public}s'
,
JSON
.
stringify
(
AppStorage
.
get
(
'currentColorMode'
)
)
??
''
)
;
}c. 在Page中通过 @StorageProp + @Watch 方式获取当前最新颜色并监听设备深色模式变化:
@ StorageProp ( 'currentColorMode' ) @ Watch ( 'onColorModeChange' ) currentMode : number = ConfigurationConstant . ColorMode . COLOR_MODE_LIGHT ;
d. 在 aboutToAppear 初始化函数中根据当前最新颜色模式刷新状态变量:
aboutToAppear
(
)
:
void
{
if
(
this
.
currentMode
==
ConfigurationConstant
.
ColorMode
.
COLOR_MODE_LIGHT
)
{
}
else
{
}
}e. 在 @Watch 回调函数中执行同样的适配逻辑:
onColorModeChange
(
)
:
void
{
if
(
this
.
currentMode
==
ConfigurationConstant
.
ColorMode
.
COLOR_MODE_LIGHT
)
{
}
else
{
}
}通过 WithTheme 可以设置三种颜色模式:跟随系统深浅色模式、固定使用浅色模式和固定使用深色模式。在 WithTheme 作用范围内,组件的样式资源值将依据指定模式,读取对应的深浅色模式系统和应用资源值,从而实现局部适配。
应用默认配置为跟随系统切换深浅色模式。如不希望应用跟随系统深浅色模式变化,可主动设置应用的深浅色风格。设置后,应用的深浅色模式固定,不会随系统改变。
说明 :应用未专门适配深色模式,直接跟随系统切换可能遇到深色模式下的显示异常,也可考虑使用该方法将本应用固定为浅色模式。
示例代码(在 onCreate 中设置固定为浅色模式):
onCreate
(
want
:
Want
,
launchParam
:
AbilityConstant
.
LaunchParam
)
:
void
{
try
{
hilog
.
info
(
DOMAIN
,
'testTag'
,
'%{public}s'
,
'Ability onCreate'
)
;
this
.
context
.
getApplicationContext
(
)
.
setColorMode
(
ConfigurationConstant
.
ColorMode
.
COLOR_MODE_LIGHT
)
;
}
catch
(
err
)
{
hilog
.
error
(
DOMAIN
,
'testTag'
,
'Failed to set colorMode. Cause: %{public}s'
,
JSON
.
stringify
(
err
)
)
;
}
hilog
.
info
(
DOMAIN
,
'testTag'
,
'%{public}s'
,
'Ability onCreate'
)
;
}如果应用调用 setColorMode 接口主动设置了深浅色,则以接口效果优先。
应用没有调用 setColorMode 接口时: 如果应用工程 dark 目录下有深色资源,则系统组件在深色模式下会自动切换成为深色。 如果应用工程 dark 目录下没有任何深色资源,则系统组件在深色模式下仍会保持浅色体验。
如果应用全部由系统组件/系统颜色开发,且想要跟随系统切换深浅色模式,应显式设置 COLOR_MODE_NOT_SET :
onCreate
(
)
:
void
{
try
{
this
.
context
.
getApplicationContext
(
)
.
setColorMode
(
ConfigurationConstant
.
ColorMode
.
COLOR_MODE_NOT_SET
)
;
}
catch
(
e
)
{
hilog
.
error
(
DOMAIN
,
'EntryAbility'
,
`
setColorMode failed, error:
${
JSON
.
stringify
(
e
)
}
`
)
;
}
}建议方法 :当应用跟随系统深色或浅色模式时,建议采用 AbilityStage 的监听回调 onConfigurationUpdate 或 Ability 的监听回调 onConfigurationUpdate 方式,主动监听系统深浅色模式变化。一旦颜色模式发生变化,应通过绑定状态变量等方法,执行特定的业务逻辑。
不推荐方法 :开发者在使用资源时,未采用监听系统深浅色模式变化的方式,而是在属性设置中通过函数返回值实现深浅色切换。例如:
getResource
(
)
:
string
{
if
(
colorMode
==
"dark"
)
{
return
"#FF000000"
}
else
{
return
"#FFFFFFFF"
}
}
build
(
)
{
Button
.
backgroundColor
(
this
.
getResource
(
)
)
}这种方式依赖于切换流程中重新执行属性设置代码,随着系统的发展和性能优化,并不能确保所有属性代码均被重新执行。因为在大部分热更新场景中,重新执行全部页面构建和属性设置代码显然是冗余的。
默认情况下,深浅色模式的切换需要执行全量重绘,包括重新设置所有组件的属性,性能开销会随着应用UI的复杂度线性增加。从API version 20开始,系统提供了一种高性能的深浅色切换流程,开发者可通过新增 metadata 配置项开启该能力,从而实现深浅色切换时开销更小。
配置方法 :在 module.json5 文件中新增 metadata 字段:
"metadata"
:
[
{
"name"
:
"configColorModeChangePerformanceInArkUI"
,
"value"
:
"true"
}
]配置此 metadata 时,必须确保在属性设置中没有通过函数返回值实现深浅色切换。
从API版本26.0.0开始, HdsNavigation 、 HdsNavDestination 、 HdsTabs 、 HdsListItemCard 四个高级组件支持高性能的深浅色切换。API版本26.0.0之前,这些高级组件暂未适配,其颜色相关属性均需使用 AbilityStage 的监听回调或 Ability 的监听回调方式来处理。
开启优化后,深浅色切换不会全量重新执行前端代码和属性设置,仅会更新、重绘必要的属性。如果开发者之前在属性设置中通过函数适配深浅色更新将不会生效,需要正确适配。以下为三个典型的适配场景及示例:
场景一:根据实时读取的深浅色模式返回不同资源值
开启优化后,应采用监听回调更新状态变量,示例代码:
import
{
Configuration
,
UIAbility
}
from
'@kit.AbilityKit'
;
export
default
class
EntryAbility
extends
UIAbility
{
onConfigurationUpdate
(
newConfig
:
Configuration
)
:
void
{
AppStorage
.
setOrCreate
(
'colorMode'
,
newConfig
.
colorMode
)
;
}
}
import
{
ConfigurationConstant
}
from
'@kit.AbilityKit'
;
@
Entry
@
Component
struct MainPage
{
@
StorageLink
(
'colorMode'
)
@
Watch
(
'colorModeChange'
)
colorMode
:
ConfigurationConstant
.
ColorMode
=
ConfigurationConstant
.
ColorMode
.
COLOR_MODE_NOT_SET
;
@
State
textColor
:
Resource
=
$r
(
"app.color.color_light"
)
;
colorModeChange
(
)
{
if
(
this
.
colorMode
===
ConfigurationConstant
.
ColorMode
.
COLOR_MODE_LIGHT
)
{
this
.
textColor
=
$r
(
"app.color.color_light"
)
}
else
{
this
.
textColor
=
$r
(
"app.color.color_night"
)
}
}
build
(
)
{
Column
(
)
{
Text
(
'fontColor'
)
.
fontColor
(
this
.
textColor
)
}
}
}场景二:根据判断自定义主题模式返回不同资源值
需要将文本内容和颜色与状态变量绑定,通过回调更新,示例代码:
export
enum
ThemeMode
{
mode1
=
0
,
mode2
}
export
class
ResourceTheme
{
fontColor
:
ResourceColor
=
this
.
getColor
(
)
;
themeMode
:
ThemeMode
=
ThemeMode
.
mode1
;
setThemeMode
(
mode
:
ThemeMode
)
{
this
.
themeMode
=
mode
}
getThemeMode
(
)
:
ThemeMode
{
return
this
.
themeMode
}
getColor
(
)
:
ResourceColor
{
if
(
this
.
themeMode
===
ThemeMode
.
mode1
)
{
return
$r
(
"app.color.color_light"
)
}
else
{
return
$r
(
"app.color.color_night"
)
}
}
}
import
{
ConfigurationConstant
}
from
'@kit.AbilityKit'
;
import
{
ResourceTheme
,
ThemeMode
}
from
'./ResourceTheme'
;
@
Entry
@
Component
struct MainPage
{
@
StorageLink
(
'colorMode'
)
@
Watch
(
'colorModeChange'
)
colorMode
:
ConfigurationConstant
.
ColorMode
=
ConfigurationConstant
.
ColorMode
.
COLOR_MODE_NOT_SET
;
resourceTheme
=
new
ResourceTheme
(
)
;
@
State
textColor
:
ResourceColor
=
this
.
resourceTheme
.
getColor
(
)
;
@
State
textContent
:
string
=
this
.
resourceTheme
.
getThemeMode
(
)
.
toString
(
)
;
colorModeChange
(
)
{
if
(
this
.
colorMode
===
ConfigurationConstant
.
ColorMode
.
COLOR_MODE_LIGHT
)
{
this
.
resourceTheme
.
setThemeMode
(
ThemeMode
.
mode1
)
}
else
{
this
.
resourceTheme
.
setThemeMode
(
ThemeMode
.
mode2
)
}
this
.
textContent
=
this
.
resourceTheme
.
getThemeMode
(
)
.
toString
(
)
this
.
textColor
=
this
.
resourceTheme
.
getColor
(
)
}
build
(
)
{
Column
(
)
{
Text
(
'ThemeMode is '
+
this
.
textContent
)
.
fontColor
(
this
.
textColor
)
}
}
}场景三:根据读取的成员变量值返回不同资源值
需要将颜色属性与状态变量绑定,通过回调更新状态变量,示例代码:
import
{
ConfigurationConstant
}
from
'@kit.AbilityKit'
;
@
Entry
@
Component
struct MainPage
{
mode
:
number
=
0
;
@
StorageLink
(
'colorMode'
)
@
Watch
(
'colorModeChange'
)
colorMode
:
ConfigurationConstant
.
ColorMode
=
ConfigurationConstant
.
ColorMode
.
COLOR_MODE_NOT_SET
;
@
State
textColor
:
Resource
=
$r
(
"app.color.color_light"
)
;
colorModeChange
(
)
{
if
(
this
.
mode
%
2
===
0
)
{
this
.
textColor
=
$r
(
"app.color.color_light"
)
}
else
{
this
.
textColor
=
$r
(
"app.color.color_night"
)
}
}
build
(
)
{
Column
(
)
{
Button
(
'change mode'
)
.
onClick
(
(
event
:
ClickEvent
)
=>
{
this
.
mode
++
}
)
Text
(
'fontColor'
)
.
fontColor
(
this
.
textColor
)
}
}
}从API version 20开始,对于有大量存量代码,之前已经通过资源配置模式或主题 WithTheme 方式实现部分深色模式适配的应用,可使用系统提供的反色能力,快速实现全量深色模式适配。这种方式虽然管理上不如资源配置和主题方式精细可控,但适配工作量更低,应用包也不会因为大量的资源配置而膨胀,同时能带来一定程度上可以接受的视觉效果。
反色能力需在优化深浅色模式切换开销使用的前提下使用。
跨进程场景如果想使用反色能力,需要 UIExtensionAbility 和对应 UIExtensionAbility 的宿主同时适配。宿主相关接口请参考 @ohos.arkui.uiExtension 。
接口说明 : 从API version 20开始,ArkUI开发框架新增 OH_ArkUI_SetForceDarkConfig 接口,提供反色能力。该功能可根据开发者自定义的反色算法,在深浅色切换时自动对颜色属性进行反色。反色能力只有在颜色属性设置为非资源值时生效,若通过 $r 设置颜色属性,则优先生效资源文件中配置的颜色值。
调用 OH_ArkUI_SetForceDarkConfig 前,需确保已加载过 OH_ArkUI_QueryModuleInterfaceByName(ARKUI_NATIVE_NODE, "ArkUI_NativeNodeAPI_1") 。
接口一定要在节点创建前的UI线程中调用。页面创建完成后,不支持通过该接口动态修改应用的反色能力生效状态。
接口仅支持进程级生效,暂不支持不同实例使用不同的反色算法。
接口仅支持CAPI接口,避免跨语言调用开销。
如果组件设置了异常值颜色或者 undefined ,反色能力不生效。
OH_ArkUI_SetForceDarkConfig ( nullptr , true , ArkUI_NodeType :: ARKUI_NODE_UNDEFINED , nullptr ) ;
build
(
)
{
Text
(
"测试反色算法"
)
.
fontColor
(
Color
.
Black
)
}uint32_t
colorInvertFunc
(
uint32_t
color
)
{
return
~
color
;
}
OH_ArkUI_SetForceDarkConfig
(
nullptr
,
true
,
ArkUI_NodeType
::
ARKUI_NODE_UNDEFINED
,
colorInvertFunc
)
;
OH_ArkUI_SetForceDarkConfig
(
nullptr
,
false
,
ArkUI_NodeType
::
ARKUI_NODE_UNDEFINED
,
nullptr
)
;
OH_ArkUI_SetForceDarkConfig
(
nullptr
,
true
,
ArkUI_NodeType
::
ARKUI_NODE_TEXT
,
nullptr
)
;
OH_ArkUI_SetForceDarkConfig
(
nullptr
,
true
,
ArkUI_NodeType
::
ARKUI_NODE_TEXT
,
colorInvertFunc
)
;不支持全局禁用反色能力的同时仅对某类组件使用反色算法。
不支持全局使用反色能力的同时仅对某类组件禁用反色算法。
使用开发者深色模式颜色资源的配置。
使用开发者为本进程中指定组件配置的反色算法。
使用开发者为本进程中所有组件配置的反色算法。
反色能力逃生通道 : 从API version 21开始,开发者可以通过主动设置 allowForceDark 属性,禁用指定组件的自动反色能力,维持深浅色切换时的原有逻辑,即使用主题或资源值切换。
深色模式的设计与适配是一个系统工程。设计层面需要遵循 易读性 、 舒适性 和 一致性 三大原则,确保深色界面在对比度、色彩语义和层级表达上满足用户需求。开发层面,应用适配深浅色模式可以通过 资源限定符(dark目录) 、 系统资源 、 监听系统配置变化 、 主动设置固定模式 、 优化切换开销 以及 反色能力 等多种方式实现。开发者应根据应用实际情况选择合适的适配策略,兼顾开发效率与用户体验,同时注意避免不推荐的适配方式,以确保应用在深浅色切换时能够正确、高效地响应。
转载声明:本文为转载整理,原文版权归原作者及原站点所有。
原文来源:CSDN HarmonyOS
原创地址:https://blog.csdn.net/QWERWTWE/article/details/164259531
原文作者:童晒钦;如有侵权,请联系管理员删除。