[Delphi] Android のダークモードに対応する方法

# Delphi はダークモードに対応していない

Delphi はスタイルで見た目をコントロールしているので、ダーク系のスタイルにすればダークモードになるよ、などと一部の記事に書いてありますが、それは残念ながら嘘です。
スタイルでダーク系の色になるのと、ダークモードの対応は全く別の話です。

↓下記の画像はダークモードが有効な端末でダーク系のスタイルにした所です。
ナビゲーションバーが白背景になっています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12977/e445746f-c169-2cae-f031-65a51b52d081.png)

↓これはデフォルトのカメラのナビゲーションバーです
ちゃんとダークモードの色（黒背景）になっています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12977/a3f90e61-1c17-8626-ba65-15c3e4c1232a.png)

# テーマ

Android ではアプリケーション全体の色調をテーマで指定します。
具体的には styles.xml で↓下のように指定します（parent(継承元)に Android の規定のテーマを指定）。

```xml
<style name="AppTheme" parent="@android:style/Theme.Material.Light.NoActionBar">
```

なお、上記の XML は Delphi が生成したものです。
parent に思いっきり **Light** が付いているテーマが書いてあります。
これはライトモード用の背景です。
OMG!

生成される styles.xml のテーマを指定できるようになっていれば良いのですが、これを制御する方法は無いようでした（TitleBar の有無だけ指定できます。知っている方が居たら教えてください）。

# Delphi でダークモードを指定する

上記の styles.xml ですが実際には下記の3ファイルが生成されます。

* styles.xml
* styles-v21.xml
* styles-v31.xml

Android のバージョンに応じて、このどれかが使われます。
v21, v31 は上記の Light テーマが使われる設定になっているので、これらが使われるとナビゲーションバーが白くなってしまいます。

かといって、styles.xml はビルド時に上書きで自動生成されるため、中身を変えるのは面倒です。
そこで、styles.xml を使わず強制的にテーマを指定してしまうのが最も簡単です。
AndroidManifest.template.xml の中身を書き換えます。

```xml
    <application
        中略
        android:theme="%theme%" ←これを
        中略
```

```xml
    <application
        中略
        android:theme="@android:style/Theme.NoTitleBar" ←こう！
        中略
```

このように色指定の無いテーマを直接指定してしまえば、自動的にシステムの色でナビゲーションバーが表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12977/792a123d-ebd2-c844-0264-6882c249dc75.png)

背景色と合致していませんが、これで一応は言い訳が立ちます。

# もっとちゃんとやる

背景色と合致してないのかっっこ悪い。
そこで、先ほど面倒と言った事をやります。

まず styles-v31.xml をプロジェクトと同じ場所に作ります。
中身は以下の通りです。

```xml:styles-v31.xml
<resources xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- Theme.Material テーマはダークモードのテーマ -->
    <style name="AppTheme" parent="@android:style/Theme.Material.NoActionBar">
        <!-- 色指定 -->
        <item name="android:navigationBarColor">ここに色を指定</item>
        <item name="android:navigationBarDividerColor">ここに色を指定</item>
        <item name="android:statusBarColor">ここに色を指定</item>
        <item name="android:windowDrawsSystemBarBackgrounds">true</item>
        <!-- ここから下はオリジナル -->
        <item name="android:windowBackground">@color/splash_background</item>
        <item name="android:windowClipToOutline">false</item>
        <!-- API 31+ specific attributes -->
        <item name="android:windowSplashScreenBackground">@color/splash_background</item>
        <item name="android:windowSplashScreenAnimatedIcon">@drawable/splash_vector</item>
        <item name="android:windowSplashScreenIconBackgroundColor">@color/splash_background</item>
    </style>
</resources>
```
「ここに色を指定」と書いてある場所（3箇所）は #ffffff という16進数表記や colors.xml の @color で指定します。

色指定について簡単に解説すると下のようになります。

|Item 名|意味|
|---|---|
|navigationBarColor|ナビゲーションバーの背景色|
|navigationBarDividerColor|ナビゲーションバーの境界線の色|
|statusBarColor|ステータスバーの背景色|

今回はスタイルの背景色と同じ #3c3c3c としました。

この styles-v31.xml を配置マネージャを使ってアプリケーションに含ませます。
配置マネージャを開くと↓このようにデフォルトの styles-v31.xml が登録されているので、これを自作の styles-v31.xml に変更します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12977/6b343fc0-1356-4353-f1d1-fa7a3e46c18c.png)

1. 自作の styles-v31.xml を配置マネージャに追加します
1. 元の styles-v31.xml のチェックを外します。
1. 自作の styles-v31.xml の情報を以下のように変更します。
リモートパス: ```res\value-v31\```
リモート名: ```styles.xml```
設定し終わった画面が下図になります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12977/97c60932-6b91-a7a2-7dbc-7d3ef420bc7f.png)

必要であれば styles-v21.xml も同じように指定してください。

これで、ビルドすると styles-v31.xml が上書きされて背景色がそろったキレイな表示になります…が…

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12977/9b8b5a7d-1835-11a5-8a75-c2fed0adad3e.png)

↓下のように線がきえない事もあります！（NavigationBarDivider の色がおかしい）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12977/e8780c06-adc8-d4ad-dc3d-4be013971df7.png)

Android が自動的に scrim と呼ばれる半透明の帯を被せてくる場合があるためです。
scrim をかけてくるかどうかは、NavigationBar の色によります（ユーザーから見てナビゲーションボタンが見えないとシステムが判断すると scrim をかけてきます）。
scrim を勝手にかけないフラグなどがあるのですが、上手く行きませんでした。
（目では解りづらいですがナビゲーションバーの色も暗くなっています)

いっそのこと、↓こんな風に線に色をつけてしまうのもありかもしれません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12977/169ee899-3995-6e35-b380-b84cf5bf4ece.png)

なお、線の色として ```#00ffff``` を指定したのですが、scrim のせいで表示された色は ```#75fbfd``` になっていました。

ちなみに、ナビゲーションバーは透過させることができるので、それを使うと NavigationBarDivider は表示されなくなり、この問題は発生しません。
ですが、ナビゲーションバーの裏側までアプリが潜り込むので Margins などを使ってナビゲーションバーに被らないようにしないといけません。

# まとめ
## 楽にやる方法
AndroidManifest.template.xml の ```android:theme``` を ```"@android:style/Theme.NoTitleBar"``` に書き換える。

## ちゃんとやる方法
自前の styles-v31.xml (必要であれば styles-v21.xml も）を用意して配置マネージャで置き換える。
線が表示されてしまう場合は、思い切って線に色を付けちゃう。