ダークテーマ

ダークテーマは、Android 10 (API レベル 29) 以上で利用可能です。それにはたくさんのメリットがあります:

  • 電力消費を大幅に節約できます(端末の画面テクノロジーに応じて異なる)。
  • 視覚障がいのあるユーザーや明るい光に過敏な方にとっての見やすさが向上します。
  • すべての人にとって暗い場所で端末を使用するのが容易になります。

ダークテーマは、Android システム UI と端末上で実行されるアプリの両方に適用されます。

Android 10 (API レベル 29) 以上でダークテーマを有効にするには、3 とおりの方法があります:

  • システム設定([設定] -> [ディスプレイ] -> [テーマ])を使ってダークテーマを有効にする。
  • クイック設定タイルを使用して、通知トレイからテーマを切り替える(有効になっている場合)。
  • Pixel 端末でバッテリー セーバー モードを選択すると、ダークテーマも同時に有効になります。その他の OEM では、この動作がサポートされている場合とサポートされていない場合があります。

アプリでダークテーマをサポートする

ダークテーマをサポートするには、アプリのテーマ(通常は res/values/styles.xml にある)を、DayNight テーマから継承する設定にする必要があります。

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

また、MaterialComponents のダークテーマ機能を使用することもできます:

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

それによりアプリのメインテーマが、システム制御の夜間モードフラグに結び付けられ、アプリがデフォルトのダークテーマになります(有効になっている場合)。

テーマとスタイル

使用するテーマとスタイルで、ライトテーマでの使用を意図した色やアイコンをハードコードしないようにしてください。テーマの属性(推奨)を使用するか、または夜間用リソースを使用してください。

テーマの属性のうち知っておくべき最も重要な 2 つをここに示します:

  • ?android:attr/textColorPrimary これは汎用のテキスト色です。ライトテーマでは黒に近い色であり、ダークテーマでは白に近い色です。これには、無効状態が含まれます。
  • ?attr/colorControlNormal 汎用のアイコン色。これには、無効状態が含まれます。

マテリアル デザイン コンポーネントを使用することをお勧めします。その色テーマシステム(テーマ属性の ?attr/colorSurface?attr/colorOnSurface など)により、適切な色に簡単にアクセスできるようになるからです。もちろん、それらの属性を自分のテーマに合わせてカスタマイズすることができます。

アプリの中でテーマを変更する

アプリ実行中にユーザーがアプリのテーマを変更できるようにしたいということがあるかもしれません。アプリのユーザーが複数のテーマの中から選択できるようにすることができます。

Android 9 以前が実行されている端末でアプリを実行する場合、推奨されるテーマ オプションは次のとおりです:

  • ライト
  • ダーク
  • バッテリー セーバーによる設定(推奨されるデフォルトのオプション)

Android 10 (API レベル 29) 以上で実行されている場合、推奨されるオプションは異なり、ユーザーがシステム デフォルトをオーバーライドできます。

  • ライト
  • ダーク
  • システム デフォルト(推奨されるデフォルトのオプション)

ユーザーがライトを選択する場合、バッテリー セーバーによって設定が変更されることはないという点に注意してください。

オプションのそれぞれが、そのままAppCompat.DayNight モードの 1 つに対応しています。

テーマを切り替えるには、AppCompatDelegate.setDefaultNightMode() を呼び出します。

強制ダーク

Android 10 には強制ダークの機能があります。これは、前述のように、デベロッパーが明示的に DayNight テーマを設定することなくすばやくダークテーマを実装するための機能です。

強制ダークでは、ライトテーマのアプリの各ビューを分析し、画面描画の前に自動的にダークテーマが適用されます。一部のデベロッパーは、強制ダークとネイティブ実装を混用することにより、ダークテーマ実装に必要な時間を節約しています。

アプリでは、アプリのテーマで android:forceDarkAllowed="true" を設定することにより強制ダークをオプトインする必要があります。この属性はシステムと AndroidX の提供するライトテーマ(Theme.Material.Light など)のすべてで設定されます。強制ダークを使用する場合、アプリを徹底的にテストし、必要に応じてビューを除外するようにしてください。

アプリでダークテーマ(Theme.Material など)を使用している場合、強制ダークは適用されません。同じように、アプリのテーマが DayNight から継承されるものである場合、自動テーマ切り替えのため、強制ダークは適用されません。

特定のビューで強制ダークを無効にする

特定のビューについて、android:forceDarkAllowed レイアウト属性を使用して、または setForceDarkAllowed() により、強制ダークを制御することができます。

ベスト プラクティス

通知とウィジェット

端末上に表示するものの直接制御することのない UI 表示については、使用するビューでホストアプリのテーマが反映されるようにすることは非常に重要です。その典型的な 2 つの例は、通知とランチャー ウィジェットです。

通知

システム提供の通知テンプレート(MessagingStyle など)を使用します。つまり、適切なビュースタイルの適用はシステム側が担当します。

ウィジェットとカスタム通知ビュー

ランチャー ウィジェットの場合、またはアプリでカスタム通知コンテンツ ビューを使用する場合、ライトテーマとダークテーマの両方でコンテンツをテストすることは非常に重要です。

注意するべきよくある落とし穴:

  • 背景色は常に明るい色だという思い込み
  • テキストの色をハードコーディングする
  • デフォルトのテキスト色を使用しながら、背景色をハードコードする
  • 静的な色のドローアブル アイコンの使用

これらのどの場合についても、ハードコードされた色ではなく適切なテーマ属性を使用してください。

起動画面

カスタム起動画面を使用している場合、選択されているテーマを反映するように変更することが必要になる場合があります。

ハードコードした色を削除してください(白を指定した背景色など)。その代わりとして ?android:attr/colorBackground テーマ属性を使用します。

ダークテーマ android:windowBackground のドローアブルは、Android Q でのみ動作することに注意してください。

構成の変更

アプリのテーマが変更されると(システム設定または AppCompat のいずれかにより)、uiMode の構成変更が発生します。つまりアクティビティが自動的に再作成されます。

場合によつては、アプリで構成変更を処理することが望ましいということがあるかもしれません。たとえば、動画再生中には、構成変更を遅らせたいかもしれません。

各アクティビティで uiMode の構成変更を処理できることを宣言することにより、アプリでダークテーマの実装を処理することができます。

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

アクティビティで構成変更を処理することを宣言すると、テーマが変更になった時点でその onConfigurationChanged() メソッドが呼び出されます。

アプリで現在のテーマが何かを調べるには、次のようなコードを実行することができます:

Kotlin

val currentNightMode = configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
when (currentNightMode) {
    Configuration.UI_MODE_NIGHT_NO -> {} // Night mode is not active, we're using the light theme
    Configuration.UI_MODE_NIGHT_YES -> {} // Night mode is active, we're using dark theme
}

Java

int currentNightMode = configuration.uiMode & Configuration.UI_MODE_NIGHT_MASK;
switch (currentNightMode) {
    case Configuration.UI_MODE_NIGHT_NO:
        // Night mode is not active, we're using the light theme
        break;
    case Configuration.UI_MODE_NIGHT_YES:
        // Night mode is active, we're using dark theme
        break;
}