鸿蒙中级课程笔记3—ArkUI进阶4—沉浸式&深色模式
深色模式(Dark Mode)又称为暗色模式,是与日常应用使用过程中的浅色模式(Light Mode)相对应的一种UI主题。深色模式相较浅色模式更加柔和,能减少亮度对用户眼睛造成的刺激和疲劳,此外深色模式能在一定程度上降低应用功耗,提升续航表现。应用深色模式适配,需遵循基本的UX设计原则,保障应用页面内容的易读性、舒适性和一致性,具体可参考深色模式设计原则。应用适配过程主要包含。
沉浸式页面实现
什么是沉浸式?
沉浸式模式通常指让应用的界面更加专注于内容,不希望用户被无关元素干扰。在移动端应用中,全屏窗口元素包括状态栏、应用界面和导航条,沉浸式页面开发通常指将应用页面延伸到状态栏和导航条。

沉浸式优点
视觉统一:使页面和避让区域的色调统一,为用户提供更好的视觉体验。
空间扩展:最大程度利用屏幕可视区域,使页面获得更大的布局空间。
沉浸专注:提供完全沉浸的体验,让用户沉浸其中,不被其他事物所干扰。
沉浸式设计原则
导航条设计原则
导航条应与页面底部的控件互不干扰,达到和谐的使用体验,同时提供沉浸式背景效果。

状态栏设计原则
采用沉浸一体化的背景设计,保证效果的整体性,避免状态栏区域被单独切割。

根据页面内状态栏区域的背景色选择合适的状态栏颜色(黑/白),保证易读性。

避免在状态栏背景区域内采用左右半区对比差异过大的颜色,导致部分状态栏信息无法阅读。

沉浸式开发步骤
实现沉浸式效果
方案一:使用Window.setWindowLayoutFullScreen()方法设置窗口为全屏模式。
方案二:设置组件的expandSafeArea属性,扩展组件的安全区域到状态栏和导航条,从而实现沉浸式。


处理避让区域与内容适配

窗口全屏模式举例
方案一:使用Window.setWindowLayoutFullScreen()方法设置窗口为全屏模式。



扩展组件安全区域模式举例
直接给设置背景色的组件添加expandSafeArea属性,使背景色延伸到状态栏和导航条即可。

Navigation和Tabs组件从API 11开始默认支持安全区避让特性,即使不设置,也默认会拓展到安全区外。
深色背景下状态栏颜色适配
当我们将应用切换到深色模式时,需要将状态栏的时间文字、图标等内容进行适配,避免状态栏内容不清晰,以此提升用户的视觉体验。

导航栏和状态栏的显/隐

深色模式适配
概述
深色模式(Dark Mode)又称为暗色模式,是与日常应用使用过程中的浅色模式(Light Mode)相对应的一种UI主题。深色模式相较浅色模式更加柔和,能减少亮度对用户眼睛造成的刺激和疲劳,此外深色模式能在一定程度上降低应用功耗,提升续航表现。
应用深色模式适配,需遵循基本的UX设计原则,保障应用页面内容的易读性、舒适性和一致性,具体可参考深色模式设计原则。应用适配过程主要包含颜色资源如字体颜色、元素背景色的适配,媒体资源如图片图标的适配,以及系统状态栏的适配,此外需要对使用了Web组件加载的Web页面进行单独处理。
本文主要介绍深色模式的适配过程,列举常见问题及对应解决方案。
实现原理
当系统切换到深色模式后,应用内可能会出现部分内容切换到深色主题的情况,例如状态栏、弹窗背景色、系统控件等,会导致应用内页面效果错乱。
为应对上述情况,需要对应用进行深色模式下的内容适配,目前该适配主要依靠资源目录。当系统对应的设置项发生变化后(如系统语言、深浅色模式等),应用会自动加载对应资源目录下的资源文件。
系统为深色模式预留了dark目录,该目录在应用创建时默认不存在。在进行深色模式适配时,需要开发者在src/main/resources中手动创建出dark目录,将深色模式所需的资源放置到该目录下。对于浅色模式所需的资源,可以放入默认存在的src/main/resources/base目录下。
说明
在进行资源定义时,需要在base目录与dark目录中定义同名的资源。例如在base/element/color.json文件中定义text_color为黑色,在dark/element/color.json文件中定义text_color为白色,那么当深浅色切换时,应用内使用了$('app.color.text_color')作为颜色值的元素会自动切换到对应的颜色,而无需使用其他逻辑判断进行控制。
一般情况下深浅色模式切换不会导致应用界面产生结构上的变化,而是保持应用界面结构一致的同时展示不同的主题配色、配图等,使得整个应用在切换到深色模式后依然保持自然美观。以下为深色模式适配的UX示例。
图1 深色模式适配UX示例图

从图1中可以看到,在应用进行深色模式适配过程中主要的适配项有颜色资源适配、媒体资源适配、状态栏适配,除此之外若应用内使用了Web组件加载Web页面,那么还需实现Web页面适配深色模式。
目前应用向用户提供的深浅色模式切换有以下两种方式。
- 应用跟随系统深浅色模式切换
实现上,需要开发者使用setColorMode()方法将ColorMode设置为COLOR_MODE_NOT_SET(未设置颜色模式),然后应用在运行过程中就可以自动感知到系统颜色模式切换,若应用完成了深浅色模式适配,将自动切换到对应的颜色模式。
- 应用内提供手动控制深浅色的开关供用户自行选择
实现上,切换深色模式需要调用setColorMode()方法将ColorMode设置为COLOR_MODE_DARK(深色模式),切换浅色模式需要将ColorMode设置为COLOR_MODE_LIGHT(浅色模式),这样就可以完成对应用深浅色的手动控制。
综上分析,深色模式适配内容如下表所示。
表1 深色模式适配内容
|
适配项 |
适配内容 |
适配方式 |
|---|---|---|
|
颜色资源适配 |
组件背景色,字体颜色等 |
|
|
媒体资源适配 |
应用内使用到的图片、图标等 |
|
|
状态栏适配 |
深浅模式下不同的状态栏表现,包括状态栏的背景色以及状态栏内时间等内容的字体颜色 |
|
|
Web内容适配 |
应用内使用Web组件加载的Web页面 |
深色模式适配

颜色资源适配
颜色资源适配是将页面元素的颜色抽离到限定词目录中,让应用在不同的深浅色模式下使用不同限定词目录中的颜色值,从而达成应用页面元素在深浅色下不同的颜色表现。若应用没有正确适配,那么在切换到深色模式后,应用页面元素对比度过低,容易造成用户识别困难。以下为颜色资源适配错误的效果示例。
表2 颜色资源适配深色模式错误效果示例
|
浅色模式 |
深色模式 |
|---|---|
|
|
|
表2中页面效果在浅色模式下显示正常,但是当切换到深色模式后 ,弹窗内文字与背景色的对比度低于5:1,用户识别弹窗内容困难。上述效果的关键问题在于使用自定义弹窗时,若未手动指定弹窗背景色,系统默认对弹窗背景色做了深浅色适配,但是系统无法对弹窗内容(特别是开发者的自定义内容)自动适配深色模式,于是当系统切换到深色模式下,弹窗背景色自动深色,而弹窗内容保持与浅色模式一致的颜色,导致内容无法看清,该类问题对应解决方案有以下两种。
- 方式一:使用系统资源(优先建议)。使用受支持的系统资源会自动适配深色模式,开发者可查看系统资源获取受支持的系统资源。


- 方式二:使用自定义主题,若开发者需要定制在深浅色模式下不同的颜色表现,就需要使用自定义主题,以下为具体实现步骤参考。
- 在src/main/resources/base/element/color.json文件中定义页面元素在浅色模式下的颜色值,此处定义了弹窗内文字在浅色模式下颜色为黑色。
{ "color": [ { "name": "text_color", "value": "#000000" } ] } - 在src/main/resources/dark/element/color.json文件中定义页面元素在深色模式下的颜色值(若有不存在的目录或文件需自行创建),此处定义了弹窗内文字在深色模式下颜色为白色。
{ "color": [ { "name": "text_color", "value": "#FFFFFF" } ] } - 在代码中引用自定义的颜色资源值,使用$r加载自定义颜色资源,系统将自动在应用深浅色变化时,加载对应限定词目录下的资源文件,从而改变页面元素的颜色完成深浅色适配。此处定义了弹窗内文字颜色为text_color。
- 在src/main/resources/base/element/color.json文件中定义页面元素在浅色模式下的颜色值,此处定义了弹窗内文字在浅色模式下颜色为黑色。
媒体资源适配
媒体资源适配即在深浅模式下采用不同颜色表现的图片或图标等媒体资源,从而达成更好的用户体验。媒体资源适配方案有以下两种。
- 方式一:若适配简单图标并且图标格式为SVG类型,那么只需要结合颜色资源适配并使用Image组件的fillColor属性(若使用Symbol则使用SymbolGlyph的fontColor属性),在不同的深浅色下设置为不同的填充色即可完成深色模式的适配。
- 方式二:若需要适配图片或适配图标,但图标不为SVG类型,那么就需要使用资源目录的方式进行深色模式的适配,具体实现步骤参考如下。
- 在src/main/resources/base/media目录中放入浅色模式下的图片资源,并按需重命名。
- 在src/main/resources/dark/media目录中放入深色模式下的图片资源(若有不存在的目录需自行创建),并保证资源名称与上一步放入的资源名称一致。
- 在代码中使用Image组件加载对应的图片资源,此处放入资源名称,如Image($r('app.media.bell'))。
状态栏适配
状态栏适配即在深浅色模式下,采用不同的状态栏背景色与字体颜色。若应用未启用沉浸式,那么默认情况下,浅色模式下状态栏为白底黑字,深色模式下状态栏为黑底白字。当应用启用了沉浸式,状态栏背景色与应用背景色保持一致,而状态栏文字默认在浅色模式下显示黑色,在深色模式下切换成白色。此时如果应用在浅色模式下设置了偏暗的背景或在深色模式下设置了偏亮的背景,都会造成状态栏背景色与状态栏字体颜色对比度过低而显示异常。
状态栏适配方案有以下两种:
- 若背景色可以做深浅色适配,则采用颜色资源适配的方案对应用背景色进行适配,背景色适配时需考虑到状态栏文字在深浅色模式下的默认表现。
// src/main/ets/pages/Index.ets @Entry @Component struct Index { // ... build() { Navigation(this.navPathStack) { // ... } .backgroundColor($r('app.color.app_background_color')) .hideTitleBar(true) // ... } } - 若背景色无法做深浅色适配,或做了深浅色适配,但是背景的沉浸式颜色与默认的状态栏文字颜色对比度较低,这种情况下需要获取当前的深浅色状态并动态设置状态栏字体颜色。有如下两种方案。
- 在EntryAbility中获取并维护当前深浅色状态,在onCreate时将当前colorMode放在AppStorage中,并在系统环境变量发生变化会触发的onConfigurationUpdate()回调中动态更新深浅色状态。
export default class EntryAbility extends UIAbility {
onCreate(_want: Want, _launchParam: AbilityConstant.LaunchParam): void {
AppStorage.setOrCreate<ConfigurationConstant.ColorMode>('currentColorMode', this.context.config.colorMode);
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate');
}
// ...
onConfigurationUpdate(newConfig: Configuration): void {
const currentColorMode: ConfigurationConstant.ColorMode | undefined = AppStorage.get('currentColorMode');
if (currentColorMode !== newConfig.colorMode) {
AppStorage.setOrCreate<ConfigurationConstant.ColorMode>('currentColorMode', newConfig.colorMode);
}
}
}

- 在页面内监听深浅色模式状态变量的变化,并根据变化后的深浅色模式来动态设置状态栏文本颜色。
@Entry
@Component
struct Index {
// ...
@StorageProp('currentColorMode') @Watch('onCurrentColorModeChange') currentColorMode: ConfigurationConstant.ColorMode =
ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET;
private windowObj: window.Window | null = null;
aboutToAppear(): void {
window.getLastWindow(this.getUIContext().getHostContext(), (err: BusinessError, data) => {
if (err.code) {
hilog.error(0x0000, 'Index', `getLastWindow failed. code=${err.code}, message=${err.message}`);
return;
}
this.windowObj = data;
})
}
onCurrentColorModeChange(): void {
if (!this.windowObj) {
return;
}
try {
if (this.currentColorMode === ConfigurationConstant.ColorMode.COLOR_MODE_LIGHT) {
this.windowObj?.setWindowSystemBarProperties({
statusBarContentColor: '#000000'
})
} else if (this.currentColorMode === ConfigurationConstant.ColorMode.COLOR_MODE_DARK) {
this.windowObj?.setWindowSystemBarProperties({
statusBarContentColor: '#FFFFFF'
})
}
} catch (error) {
let err = error as BusinessError;
hilog.error(0x0000, 'Index', `setWindowSystemBarProperties failed, error code=${err.code}, message=${err.message}`);
}
}
// ...
build() {
// ...
}
}
固定颜色模式

Web页面适配深色模式

Web页面的内容不会自动跟随系统颜色模式进行切换。
若需要Web页面进行深浅色适配,就需要在Web页面内通过媒体查询的方式单独设置深色模式下的页面样式,并通过Web组件的darkMode()属性来控制Web页面是否启用深色模式。
具体实现开发者可参考:Web组件设置深色模式。
常见问题
自定义弹窗无法跟跟随系统深浅色切换或者只有部分内容跟随深浅色切换
可能原因
如果自定义弹窗没有设置背景色,那么系统对弹窗默认的背景色做了深色模式适配,但弹窗的内容特别是自定义内容,无法自动适配深色模式,导致无法跟随系统深浅色模式的切换发生变化。
解决措施
参考上述适配过程对弹窗内容进行适配。
媒体资源适配时显示文件已存在
问题现象
当开发者适配媒体资源时(如图片图标等),出现对应资源在另外的目录已存在的弹窗。
解决措施
直接点击弹窗内的“continue”按钮即可,并不会导致适配错误。
更多推荐



所有评论(0)