"微信都支持暗黑模式了,咱 App 也適配一波!"

Zhujiang |作者

承香墨影 |校對

https://juejin.im/post/5e95633951882573c2192501 |原文

一、前言

第一次聽到暗黑模式的時候,感覺好酷啊,聽着就好看(也不知道我怎麼聽出來的)。

蘋果在前幾年就有暗黑模式的風聲,好像是在 iOS11、iOS12 的時候就說要推出了,結果愣生生等到了 iOS13 暗黑模式才推出。

iOS13 推出到現在已經大半年了,系統應用沒得說,完美適配,三方應用也都陸續支持了暗黑模式,就連微信在前段時間的更新中也實現了暗黑模式,先來欣賞下微信的暗黑模式吧!

二、正文

既然蘋果實現了暗黑模式,那麼安卓肯定不能落下啊,所以在 Android-10(Q-API Level 29)中支持了暗黑模式,不過官方的叫法爲 Dark theme,怎麼翻譯都行,先來看一下官方對暗黑模式的定義吧:

這裏就不逐字翻譯了,主要來說一下暗黑模式的好處吧:

  • 手機的屏幕目前大部分都已經升級成了 OLED 屏幕(當然還有一部分手機還是 LCD 屏幕),OLED 屏幕顯示黑色的時候不會發光,可以大大減小功耗。

  • 提高了弱視用戶和對強光敏感的用戶的可見性。

  • 使任何人在昏暗的環境中都更容易使用設備。

怎樣開啓暗黑模式就不多說了,手機廠商不同,開啓方式不同,各大手機廠商魔改的系統,有時候真的找不到在哪設置,自行百度一下吧。

設置暗黑主題

爲了支持 Dark 主題,必須將應用的主題設置爲,繼承自 DayNight 主題(res/values/styles.xml):

<style name="AppTheme" 
  parent="Theme.AppCompat.DayNight">

還可以使用 MaterialComponents 的深色主題:

https://material.io/develop/android/theming/dark

<style name="AppTheme" 
  parent="Theme.MaterialComponents.DayNight">

這會將應用程序的主主題與系統控制的夜間模式標誌相關聯,併爲應用程序提供默認的深色主題(啓用時)。當系統的主題切換時,應用也會隨之切換主題。

“這就完了?”
“對啊,主題就設置完了。”
“那我如果想要自定義主題呢?”

大家日常開發中肯定會有這種需求,官方定義的主題不完全能滿足我們的需求,這時候就需要來自定義主題了,谷歌也爲我們想到了這一點。

普通模式下咱們不需要動,該怎麼寫怎麼寫,然後在 res 下再新建一個 values-night 的文件夾,然後把你自定義的放進去,name 名爲 styles.xml 中對應的主題名稱就可以了。

來看一下吧:

這樣設置完就 OK 了。

“不對啊,這只是主題變了,那我的 Activity 的背景、字體的顏色、圖片等等該怎麼辦呢?”

“彆着急,下面就要說了。”

這裏一定要注意,主題和樣式應避免在淺色主題下使用硬編碼的顏色或圖標,應該使用主題屬性(首選)或夜間限定的資源。來了解下兩個最重要的主題屬性吧:

  • ?android:attr/textColorPrimary:這是一種通用的文本顏色。淺色主題爲近黑色,深色主題爲近白。它包含禁用狀態。

  • ?attr/colorControlNormal:通用圖標顏色。它包含禁用狀態。

當然肯定不是必須要使用上面官方提供的兩個主題屬性,想自定義就自定義啊!眼睛尖的可能已經看見了,上面的圖片中的 values-night 中除了放有 style.xml 外還有 colors.xml,對,沒錯,咱們只要把顏色信息放入到 colors.xml 中然後根據需求寫上兩個顏色就好了。

但還是建議使用 Material Design Components,因爲它的顏色主題系統 (例如主題屬性 ?attr/colorSurface 和 ?attr/colorOnSurface)可以輕鬆訪問合適的顏色。

https://material.io/develop/android/

https://material.io/develop/android/theming/color/

“大哥,背景顏色和字體顏色我知道怎麼改了,圖片呢?圖片咋辦啊!”

“來了來了,猴急猴急的!”

圖片設置其實和顏色差不多,也是兩套資源,比如你的 drawable 文件夾下有一張 aaa.jpg 的圖片,你想在暗黑模式下換成另外一張圖片,那麼你就可以新建一個 drawable-night 的文件夾,在裏面放上你的另外一張圖片就行了,注意, 圖片名稱一定要和 drawable 中的對應。

同理,drawable-xhdpidrawable-xxhdpi 就是再建兩個文件夾:drawable-night-xhdpidrawable-night-xxhdpi,然後放入對應圖片就行了。

“什麼?你想看一下效果?那好吧,如你所願,這真的是你,要是別人我都不讓他看。。。。”

怎麼樣,效果還可以吧?

四、應用內修改主題

“我還想自己主動切換,不想隨着系統換才換”

“來,你過來,你還想幹啥,說,來來來,說吧”

“我就想自己主動設置是否跟隨系統切換主題。。。我看好多應用都有這個功能”

哎,既然你發自內心的問了,那我就大發慈悲的告訴你:當然可以喲!

一般來說應用都會有幾個選項供你選擇,分別是:普通模式、暗黑模式、跟隨系統,對吧?
谷歌也給了我們這幾個選項,可以直接進行設置:

  • Light - MODE_NIGHT_NOhttps://developer.android.google.cn/reference/androidx/appcompat/app/AppCompatDelegate.html#MODE_NIGHT_NO

  • 黑暗 - MODE_NIGHT_YEShttps://developer.android.google.cn/reference/androidx/appcompat/app/AppCompatDelegate.html#MODE_NIGHT_YES

  • 由省電模式設定 - MODE_NIGHT_AUTO_BATTERYhttps://developer.android.google.cn/reference/androidx/appcompat/app/AppCompatDelegate.html#MODE_NIGHT_AUTO_BATTERY

  • 系統預設 - MODE_NIGHT_FOLLOW_SYSTEMhttps://developer.android.google.cn/reference/androidx/appcompat/app/AppCompatDelegate.html#MODE_NIGHT_FOLLOW_SYSTEM

咱們剛纔所說的就是 Light、黑暗和系統預設,省電模式這裏就不寫, 如果有需求可以進行實驗。

切換主題的方法也很簡單,直接調用下面方法就行:

AppCompatDelegate.setDefaultNightMode()

就一行代碼就行了,參數需要傳入上面的四種模式之一。

來吧,那就寫一下代碼吧,按照上面的要求寫三個按鈕,分別來實現普通模式、暗黑模式和系統模式吧:

override fun onClick(v: View) {        
  when(v.id){            
    R.id.btnLight ->{                
      setDefaultNightMode(MODE_NIGHT_NO)            
    }            
    R.id.btnDark ->{                
      setDefaultNightMode(MODE_NIGHT_YES)            
    }            
    R.id.btnDefault ->{                
      setDefaultNightMode(MODE_NIGHT_FOLLOW_SYSTEM)            
    }
  }
}

這個代碼應該不需要解釋了,上面解釋的已經夠多了。來看一下實現效果吧:

五、配置變更

“大哥,我又想了下,如果有的頁面正在播放視頻,我想要延遲配置更改該怎麼辦啊?”

“來,你過來小兄弟,逗着大哥玩呢?這一天天的!”

來吧,有需求就得有實現,谷歌大大已經爲我們都想好了,應用可以通過聲明每個 Activity 可以處理 uiMode 配置更改來處理 Dark 主題本身的實現:https://developer.android.google.cn/reference/android/content/res/Configuration#uiMode

<activity
  android:name=".MyActivity"    
  android:configChanges="uiMode" />

當 Activity 聲明它處理配置更改時,onConfigurationChanged() 將在主題更改時調用其方法。要檢查當前主題是什麼,應用可以運行如下代碼:Activity#onConfigurationChanged(Configuration)

val currentNightMode = configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
when (currentNightMode) {    
  Configuration.UI_MODE_NIGHT_NO -> {} // 夜間模式未啓用,我們正在使用淺色主題    
  Configuration.UI_MODE_NIGHT_YES -> {} // 夜間模式啓用,我們使用的是深色主題  
}

六、總結

文章到這裏基本結束了,谷歌用行動告訴我們儘量不要硬編碼,出來混都是要還的,硬編碼一時爽,一直硬編碼一直爽,哈哈哈。

文中源碼已上傳 Github:https://github.com/zhujiang521/Skin

推薦閱讀:

高幀屏-120Hz,對我們寫 App 有什麼影響?

OkHttp 支持 WebSocket,從源碼到協議的分析

Git 如何優雅的回退代碼?

本文對你有幫助嗎?留言、轉發、點好看是最大的支持,謝謝!


公衆號後臺回覆成長『成長』,將會得到我準備的學習資料。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章