一、深色模式
深色模式(Dark Mode)又稱之為暗色模式,是與日常應用使用過程中的淺色模式(Light Mode)相對應的一種UI主題。深色模式滿足更多個性化的需求,基于人因研究設計了深色模式下舒適的顏色范圍。深色模式開啟后,淺色主題應用界面背景會變成深色,而文字、圖標等前景會變成淺色。深色模式的界面上內容更加突出、在 OLED 屏幕的設備上更加省電、能夠給用戶帶來視覺舒適感和沉浸式體驗。
二、設計原則
由于深色模式下屏幕總發(fā)光量更少,部分用戶傾向于在暗環(huán)境使用深色模式;但同時,也有相當比例的用戶出于對深色風格的喜愛或出于節(jié)能省電的需求(深色模式下OLED屏幕功耗更低)而全天開啟深色模式。
因此,在深色模式的設計中需要考慮在全天不同環(huán)境下清晰易讀,同時特別關注在暗環(huán)境的使用舒適性,另外還需要保持與淺色模式在層級感知、色彩語義方面的一致性。
1、易讀性
深色模式下為了保證清晰易讀,文本、圖標等前景與深色背景之間仍需要滿足最小對比度要求。
場景 | 推薦 | 一般 | 不建議 |
---|---|---|---|
大字號(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 |
建議表示活動狀態(tài)的可交互控件 | 控件背板與背景之間對比度不小于2.2:1 | \ | \ |
對于同時使用多個多彩色用于表示不同狀態(tài)或用于相鄰位置時,在適應對比度要求的基礎上,還需要滿足色彩差異要求:
場景 | 要求 |
---|---|
一般要求 | 兩個需要區(qū)分的顏色或相鄰顏色,色彩差異△Euv >= 20 |
無障礙要求 | 考慮色盲群體無障礙需要,使用色盲模擬器后,兩個要區(qū)分的顏色或相鄰顏色,色彩差異△Euv >= 20 |
2、舒適性
由于部分用戶時長在暗環(huán)境選擇使用深色模式,深色模式建議遵從以下原則以盡量避免刺眼等舒適性問題。
要求 | 推薦 | 一般 | 不合格 |
---|---|---|---|
文字對比度上限 | 文字需要謹慎使用大于17.6:1的高對比度 | 15.7:1-17.6:1 | 大于17.6:1 |
小圖片、圖標背板 | 對比度建議不大于15.7:1 | \ | \ |
淺色控件使用與適配 | 同時滿足以下三條要求: -深色模式下應用應切換為深色背景 -應用內避免黑白跳轉的頁面 -頁面上不建議使用大面積白色圖片或背景 |
深色模式下應用切換為深色背景,但存在黑白跳轉的頁面或大面積白色圖片 | 深色模式下應用仍為淺色界面 |
3、一致性
由于多數(shù)應用設計以及用戶使用的默認模式為淺色模式,建議在深色模式的設計中需要注意與淺色模式保持一致性,主要包括以下幾方面
層級的一致性 淺色模式下明度不是唯一的表達層級的視覺線索,而在深色模式尤其是在黑色背景上,用戶對投影的感知程度降低,通常使用明度表達層級,因此不同層級之間需要有一定的明度區(qū)分,并與淺色模式感知一致
色彩語義的一致性 深色模式下,表示警示、通話等具有語義信息的顏色需要保持一致,可以在保持色相一致的前提下對明度進行調整
同類控件風格一致性 深色模式下,在用應用或不同頁面中的同類控件,視覺風格(顏色、形狀等)保持一致
三、實現(xiàn)原理
當系統(tǒng)切換到深色模式后,應用內可能會出現(xiàn)部分內容切換到深色主題的情況,例如狀態(tài)欄、彈窗背景色、系統(tǒng)控件等,會導致應用內頁面效果錯亂。
為應對上述情況,需要對應用進行深色模式下的內容適配,目前該適配主要依靠資源目錄。當系統(tǒng)對應的設置項發(fā)生變化后(如系統(tǒng)語言、深淺色模式等),應用會自動加載對應資源目錄下的資源文件。
系統(tǒng)為深色模式預留了dark目錄,該目錄在應用創(chuàng)建時默認不存在,在進行深色模式適配時,需要開發(fā)者在src/main/resources中手動地創(chuàng)建出dark目錄,將深色模式所需的資源放置到該目錄下。對于淺色模式所需的資源,可以放入默認存在的src/main/resources/base目錄下。
說明
在進行資源定義時,需要在base目錄與dark目錄中定義同名的資源。例如在base/element/color.json文件中定義text_color為黑色,在dark/element/color.json文件中定義text_color為白色,那么當深淺色切換時,應用內使用了$('app.color.text_color ')作為顏色值的元素會自動切換到對應的顏色,而無需使用其他邏輯判斷進行控制。
一般情況下深淺色模式切換不會導致應用界面產生結構上的變化,而是頁面結構一致但是采用不同的主題配色、配圖等,使得整個應用在切換到深色模式后依然保持自然美觀,以下為深色模式適配的UX示例。
從上圖中可以看到,在應用進行深色模式適配過程中主要的適配項有顏色資源適配、媒體資源適配、狀態(tài)欄適配
適配項 | 適配內容 | 適配方式 |
---|---|---|
顏色資源適配 | 組件背景色,字體顏色等 | 使用受支持的系統(tǒng)資源 使用color.json資源文件 |
媒體資源適配 | 應用內使用到的圖片、圖標等 | SVG類型圖標可使用fillColor()屬性 使用media資源目錄 |
狀態(tài)欄適配 | 深淺模式下不同的狀態(tài)欄表現(xiàn),包括狀態(tài)欄的背景色以及狀態(tài)欄內時間等內容的字體顏色 | 對應用背景色進行深淺色適配 根據(jù)當前深淺色狀態(tài)動態(tài)設置狀態(tài)欄字體顏色 |
四、適配方案
1、顏色資源適配
顏色資源適配是將頁面元素的顏色抽離到限定詞目錄中,讓應用在不同的深淺色模式下使用不同限定詞目錄中的顏色值,從而達成應用頁面元素在深淺色下不同的顏色表現(xiàn)。
方式一:使用系統(tǒng)資源(優(yōu)先建議)。
方式二:使用自定義主題,若開發(fā)者需要定制在深淺色模式下不同的顏色表現(xiàn),就需要使用自定義主題,以下為具體實現(xiàn)步驟參考。
1.1、在src/main/resources/base/element/color.json文件中定義頁面元素在淺色模式下的顏色值,此處定義了彈窗內文字在淺色模式下顏色為黑色。
{
"color": [
{
"name": "text_color",
"value": "#000000"
}
]
}
1.2、在src/main/resources/dark/element/color.json文件中定義頁面元素在深色模式下的顏色值(若有不存在的目錄或文件需自行創(chuàng)建),此處定義了彈窗內文字在深色模式下顏色為白色。
{
"color": [
{
"name": "text_color",
"value": "#FFFFFF"
}
]
}
1.3、在代碼中引用自定義的顏色資源值,使用$r加載自定義顏色資源,系統(tǒng)將自動在應用深淺色變化時,加載對應限定詞目錄下的資源文件,從而改變頁面元素的顏色完成深淺色適配。
build() {
Column({space:30}) {
Text('授權成功')
.fontWeight(FontWeight.Bold)
.fontColor($r('app.color.text_color')) // 引用適配了深色模式的資源值
Text('授權碼:441132')
.fontColor($r('app.color.text_color')) // 引用適配了深色模式的資源值
Row({ space: 30 }) {
Button('復制', { buttonStyle: ButtonStyleMode.TEXTUAL })
.fontColor($r('app.color.text_color')) // 引用適配了深色模式的資源值
Button('確定', { buttonStyle: ButtonStyleMode.TEXTUAL })
}
}
.padding(20)
1.4、顏色資源完成深色模式適配效果示例
2、媒體資源適配
媒體資源適配即在深淺模式下采用不同顏色表現(xiàn)的圖片或圖標等媒體資源,從而達成更好的用戶體驗,以下為應用內的圖標未適配深色模式的效果示例,未適配內容以黃虛線框出。
方式一:若適配簡單圖標并且圖標格式為SVG類型,那么只需要結合顏色資源適配并使用Image組件的fillColor屬性(若使用Symbol則使用SymbolGlyph的fontColor屬性),在不同的深淺色下設置為不同的填充色即可完成深色模式的適配。
方式二:若需要適配圖片或適配圖標但圖標不為SVG類型,那么就需要使用資源目錄的方式進行深色模式的適配,具體實現(xiàn)步驟參考如下。
2.1、在src/main/resources/base/media目錄中放入淺色模式下的圖片資源,并按需重命名。
2.2、在src/main/resources/dark/media目錄中放入深色模式下的圖片資源(若有不存在的目錄需自行創(chuàng)建),并保證資源名稱與上一步放入的資源名稱一致。
2.3、在代碼中使用Image組件加載對應的圖片資源,此處放入資源名稱為banner。
build() {
Column() {
Image($r("app.media.banner")) // 引用適配了深色模式的圖標資源
.width('100%')
.borderRadius(12)
.objectFit(ImageFit.Cover)
}
.height('100%')
.width('100%')
}
2.4、應用內圖標未適配深色模式效果示例
3、狀態(tài)欄適配
狀態(tài)欄適配即在深淺色模式下,采用不同的狀態(tài)欄背景色與字體顏色。若應用未啟用沉浸式,那么默認情況下,淺色模式下狀態(tài)欄為白底黑字,深色模式下狀態(tài)欄為黑底白字。當應用啟用了沉浸式,狀態(tài)欄背景色與應用背景色保持一致,而狀態(tài)欄文字會默認在淺色模式下保持黑色,而在深色模式下保持白色,若應用在淺色模式下設置了深色背景或在深色模式下設置了淺色背景,都會導致狀態(tài)欄背景色與狀態(tài)欄字體顏色對比度過低,導致顯示異常。錯誤效果示例如下圖,應用設置了沉浸式并在淺色模式下具有純黑色的背景色,導致狀態(tài)欄的日期電量等文本內容無法看清。
方式一:若可以將背景色做深淺色適配,則采用顏色資源適配的方案對應用背景色進行適配,背景色適配時需考慮到狀態(tài)欄文字在深淺色模式下的默認表現(xiàn)。
方式二:若背景色無法做深淺色適配,或做了深淺色適配,但是沉浸式顏色與默認的狀態(tài)欄文字顏色對比度較低,這種情況下需要獲取當前的深淺色并動態(tài)設置狀態(tài)欄字體顏色。具體實現(xiàn)步驟參考如下。
3.1、在EntryAbility中獲取并維護當前深淺色狀態(tài),在onCreate時將當前colorMode放在AppStorage中,并在配置變化的onConfigurationUpdate()回調中動態(tài)更新深淺色狀態(tài)。
// src/main/ets/entryability/EntryAbility.ets
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);
}
}
}
3.2、在頁面內監(jiān)聽深淺色模式狀態(tài)變量的變化,并根據(jù)變化后的深淺色模式來動態(tài)設置狀態(tài)欄文本顏色。
@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(getContext(this), (err: BusinessError, data) => {
this.windowObj = data;
})
}
onCurrentColorModeChange(): void {
if (!this.windowObj) {
return;
}
if (this.currentColorMode === ConfigurationConstant.ColorMode.COLOR_MODE_LIGHT) {
this.windowObj?.setWindowSystemBarProperties({
statusBarContentColor: '#FFFFFF'
})
} else if (this.currentColorMode === ConfigurationConstant.ColorMode.COLOR_MODE_DARK) {
this.windowObj?.setWindowSystemBarProperties({
statusBarContentColor: '#000000'
})
}
}
// ...
build() {
// ...
}
}
3.3、狀態(tài)欄適配深色模式后效果