OSバージョン間の差分を考慮したAndroidのThemeの定義方法について整理してみました。基本的にはAppCompatライブラリの定義方法を参考にしています。
Themeについては以下の公式ドキュメントをご参照ください。
以降は、Android 7.0で追加されたマルチウィンドウ、Android 9で追加されたディスプレイ カットアウトに関する設定をThemeに追加するケースを例とします。
1. Themeの定義専用のリソースファイルを作成し、Themeの定義を移動する
Android Studioでプロジェクトを作成した後に特に変更を加えていなければ、Themeはstyles.xml
に定義されています。
StyleとThemeはともに<style>
タグによって定義されますが、これらを同一ファイル内に定義すると、どれがStyleの定義でどれがThemeの定義かが分かりづらくなります。
そこで、Themeの定義専用にthemes_base.xml
とthemes.xml
というファイルをres/values
ディレクトリに作成します。
ファイルを作成したら、styles.xml
のThemeに関する定義をthemes_base.xml
に移動します。リソース名はAppTheme
からBase.MyAppTheme
に変更します。
themes_base.xml
には全OSバージョン共通で適用するThemeを定義します。
<!-- res/values/themes_base.xml --> <?xml version="1.0" encoding="utf-8"?> <resources> <!-- Base application theme. --> <style name="Base.MyAppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> <!-- Customize your theme here. --> <item name="colorPrimary">@color/colorPrimary</item> <item name="colorPrimaryDark">@color/colorPrimaryDark</item> <item name="colorAccent">@color/colorAccent</item> </style> </resources>
次に、Base.MyAppTheme
を継承したThemeをthemes.xml
に定義します。リソース名はMyAppTheme
とします。
Themeの継承には<style>
タグのparent
属性を使用します。
<!-- res/values/themes.xml --> <?xml version="1.0" encoding="utf-8"?> <resources> <style name="MyAppTheme" parent="Base.MyAppTheme" /> </resources>
そして、このMyAppTheme
をマニフェストファイルの<application>
タグのandroid:theme
属性に指定します。
これにより、MyAppTheme
に定義した内容がアプリケーション全体のThemeとして適用されます。
2. 各OSバージョンに対応する代替リソースを作成する
ベースとなるリソースファイルの準備ができたので、次は各OSバージョンに対応する代替リソースを作成します。
代替リソースについては以下の公式ドキュメントをご参照ください。
代替リソースを追加するディレクトリのsuffixは、OSバージョンではなくAPI Levelを指定します。OSバージョンとサポートされるAPI Levelは1対1で対応しています。
両者の対応関係は以下の公式ドキュメントをご参照ください。
上記のドキュメントから、Android 7.0でサポートされるAPI Levelは24、Android 9でサポートされるAPI Levelは28と分かります。
よって、res/values-v24
ディレクトリとres/values-v28
ディレクトリを作成し、各ディレクトリにthemes.xml
を作成します。
なお、themes_base.xml
を作成する必要はありません。
その後、先ほどと同様にthemes.xml
にThemeを定義していきます。
res/values-v24
ディレクトリ内のthemes.xml
には、Base.MyAppTheme
を継承したBase.V24.MyAppTheme
を作成し、マルチウィンドウの設定を追加します。
また、Base.V24.MyAppTheme
を継承したMyAppTheme
を作成します。
<!-- res/values-v24/themes.xml --> <?xml version="1.0" encoding="utf-8"?> <resources> <style name="MyAppTheme" parent="Base.V24.MyAppTheme" /> <style name="Base.V24.MyAppTheme" parent="Base.MyAppTheme"> <!-- Android 7.0(API Level 24)以降で使用可能な属性を追加する --> <item name="android:resizeableActivity">false</item> </style> </resources>
同様に、res/values-v28
ディレクトリ内のthemes.xml
には、Base.V24.MyAppTheme
を継承したBase.V28.MyAppTheme
を作成し、ディスプレイ カットアウトの設定を追加します。
また、Base.V28.MyAppTheme
を継承したMyAppTheme
を作成します。
<!-- res/values-v28/themes.xml --> <?xml version="1.0" encoding="utf-8"?> <resources> <style name="MyAppTheme" parent="Base.V28.MyAppTheme" /> <style name="Base.V28.MyAppTheme" parent="Base.V24.MyAppTheme"> <!-- Android 9(API Level 28)以降で使用可能な属性を追加する --> <item name="android:windowLayoutInDisplayCutoutMode">never</item> </style> </resources>
上記の結果、各OSバージョンに対応するMyAppTheme
の継承関係は以下の通りになります。
ポイントは、より新しいOSバージョンに対応するMyAppTheme
が古いOSバージョンに対応するThemeを全て含んでいる点です。
・全OSバージョン共通
Base.MyAppTheme
└MyAppTheme
・Android 7.0(API Level 24)以降
Base.MyAppTheme
└Base.V24.MyAppTheme
└MyAppTheme
・Android 9(API Level 28)以降
Base.MyAppTheme
└Base.V24.MyAppTheme
└Base.V28.MyAppTheme
└MyAppTheme
代替リソースを利用する場合、例えばAndroid 7.0の端末であればres/values-v24
のthemes.xml
が利用されます。
多言語化などを行うときは共通する定義が基本的にないので問題ないのですが、Themeの場合は共通する定義があるので、何も考えないと全OSバージョン共通のThemeを何度も定義しなければなりません。
今回のような形でThemeを定義することによって、OSバージョン間の差分を考慮してAndroidのThemeが適用できるようになります。