ダークテーマ

ダークテーマは、Android 10(API レベル 29)以上で利用可能です。ダークテーマには、次のような多くのメリットがあります。

  • 電力使用量を大幅に削減できます(削減できる量はデバイスの画面テクノロジーに左右されます)。
  • 視力の低いユーザーや明るい光に敏感なユーザーにとって、画面の見やすさが向上します。
  • すべてのユーザーにとって、暗い場所でのデバイスの使いやすさが向上します。

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

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

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

WebView コンポーネントを使用してウェブベースのコンテンツにダークテーマを適用する手順については、WebView のウェブ コンテンツをダークテーマ化するをご覧ください。

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

ダークテーマをサポートするには、アプリのテーマ(通常は 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 など)を通して適切な色を簡単に利用できるので、おすすめです。もちろん、自分のテーマでそれらの属性をカスタマイズできます。

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

アプリの実行中にユーザーがアプリのテーマを変更できるようにしたい場合があります。ユーザーがテーマを選択できるようにアプリを設定できます。推奨されるオプションを次に示します。

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

各オプションは、AppCompat.DayNight モードのいずれかに直接マッピングされています。

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

フォースダーク

Android 10 では、デベロッパー向けのフォースダーク機能を使用して、ダークテーマを簡単に実装できます。上記の説明のように、DayNight テーマを明示的に設定する必要はありません。

フォースダーク機能は、ライトテーマが設定されているアプリの各ビューを分析し、ダークテーマを自動的に適用してから画面に描画します。一部のデベロッパーは、フォースダークとネイティブ実装を組み合わせて使用することにより、ダークテーマを実装するのに必要な時間を短縮しています。

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

アプリがダークテーマ(Theme.Material など)を使用している場合、フォースダークは適用されません。同様に、アプリのテーマが DayNight テーマを継承している場合、テーマは自動的に切り替えられるので、フォースダークは適用されません。

ビューでフォースダークを無効にする

android:forceDarkAllowed レイアウト属性または setForceDarkAllowed() を使用することにより、特定のビューに対するフォースダークの適用を制御できます。

ウェブ コンテンツ

ウェブベースのコンテンツでダークテーマを使用する方法については、WebView のウェブ コンテンツにダークテーマを適用するをご覧ください。ダークテーマが適用される例については、GitHub の WebView デモをご覧ください。

おすすめの方法

以下のセクションでは、ダークテーマの実装に関するおすすめの方法を紹介します。

通知とウィジェット

デバイスに表示するが直接制御しない UI サーフェスでは、使用するすべてのビューにホストアプリのテーマを反映させることが重要です。通知とランチャー ウィジェットはその好例です。

通知

システムによって提供される通知テンプレート(MessagingStyle など)を使用します。つまり、適切なビュースタイルの適用はシステムが行います。

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

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

陥りやすい落とし穴:

  • 背景色は常に明るいと仮定する
  • テキストの色をハードコードする
  • ハードコードされた背景色を設定し、さらにデフォルトのテキストの色を使用する
  • 静的な色の描画可能なアイコンを使用する

上記のいずれの場合も、ハードコードされた色ではなく、適切なテーマ属性を使用してください。

起動画面

アプリにカスタムの起動画面がある場合は、選択したテーマを反映させるために変更が必要になることがあります。

ハードコードされた色(たとえば、背景色が白に指定されていることがあります)をすべて削除します。代わりに ?android:attr/colorBackground テーマ属性を使用します。

ダークテーマを適用した android:windowBackground ドローアブルは、Android 10 でのみ機能することにご注意ください。

構成の変更

アプリのテーマが(システム設定または 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;
}