🐈

Androidのスタイルリソースとは何か?

2021/05/16に公開

概要

Android には <style> というタグで記述される「スタイルリソース」というものが存在します。
このスタイルリソースにはいくつかの種類が存在していますが、あまり公式のドキュメントにおいてそれらが具体的にどのように利用されるべきなのかが言及されていません。
この文書では AndroidX のコード中でのそれらの具体的な定義や利用方法などを例示しながら、実際の Android アプリケーション開発でどのように定義していけば良いのかを見ていきたいと思います。

スタイルリソースの種類

スタイルリソースとして定義されるリソースにはいくつかの種類があります。
具体的には下記になります。

  • テーマ
  • スタイル
  • テーマオーバーレイ
  • テキストアピアランス

これらのリソースは特徴的なプリフィックスを名前(name属性)に持ち、 Android Studio での名前補完候補にも登場しますので見かけたことのある方も多いのではないかと思います。
各リソースのプリフィックスを下表に示します。

種類 プリフィックス
テーマ Theme.
スタイル Widget.
テーマオーバーレイ ThemeOverlay.
テキストアピアランス TextAppearance.

テーマ

具体的な例をいくつか見ていきましょう。
まずは「テーマ」から。

<style name="Theme.AppCompat.Light.NoActionBar">
    <item name="windowActionBar">false</item>
    <item name="windowNoTitle">true</item>
</style>

https://github.com/androidx/androidx/blob/androidx-main/appcompat/appcompat/src/main/res/values/themes.xml#L45-L48

これはアプリケーションのテーマの継承元としてよく利用される AndroidX AppCompat で定義されるテーマです。このテーマはTheme.AppCompat.Lightを継承した上でwindowActionBarwindowNoTitleを書き換えたものということですね。

スタイル

次は「スタイル」を見ましょう。

<style name="Widget.AppCompat.TextView" parent="Base.Widget.AppCompat.TextView"/>

https://github.com/androidx/androidx/blob/907e04af2a88cdf288e132af208d396449186faf/appcompat/appcompat/src/main/res/values/styles.xml#L184

これは AppCompat をベースにしたテーマを利用した場合に適用される標準的なTextViewのスタイルになります。このスタイルはBase.Widget.AppCompat.TextViewという別のスタイルを継承しています。スタイルはテーマや API Level に応じて継承元のスタイルを変える必要があり、parent属性によって継承を行うという記述がよく見られます。

テーマオーバーレイ

次は「テーマオーバーレイ」を見ましょう。

<style name="ThemeOverlay.AppCompat.Dialog.Alert" parent="Base.ThemeOverlay.AppCompat.Dialog.Alert" />

https://github.com/androidx/androidx/blob/907e04af2a88cdf288e132af208d396449186faf/appcompat/appcompat/src/main/res/values/themes.xml#L99

テーマオーバーレイやActionBarAlertDialogを利用する場合によく利用されます。ここではAlertDialog用のテーマオーバーレイの定義をあげてみました。こちらもスタイルと同様にparent属性による継承元指定が利用されています。

テキストアピアランス

最後に「テキストアピアランス」を見ましょう。

<style name="TextAppearance.AppCompat.Widget.Button" parent="Base.TextAppearance.AppCompat.Widget.Button" />

https://github.com/androidx/androidx/blob/907e04af2a88cdf288e132af208d396449186faf/appcompat/appcompat/src/main/res/values/styles.xml#L296

これは標準的なButton用のテキストの見た目を表す定義になります。せっかくなので継承元の定義も見てみましょう。

<style name="Base.TextAppearance.AppCompat.Button">
    <item name="android:textSize">@dimen/abc_text_size_button_material</item>
    <item name="android:textAllCaps">true</item>
    <item name="android:textColor">?android:textColorPrimary</item>
</style>

https://github.com/androidx/androidx/blob/907e04af2a88cdf288e132af208d396449186faf/appcompat/appcompat/src/main/res/values/styles_base_text.xml#L97-L101

いかにも「テキストの見た目を表現するために用意されているものである」という感じの定義になっています。

スタイルリソースの使い方

ここまででスタイルリソースにはいくつかの種類があり、それらが実際に AndroidX ライブラリの中で定義されているということを見てきました。本項ではそれらのスタイルリソースが「どのように使われるのか?」あるいは「どのように使えば良いか?」を見ていきたいと思います。

テーマ属性

AndroidX や Material Components for Android の定義するデフォルトのスタイルリソースの多くは、各テーマのテーマ属性のデフォルト値として設定されています。
AppCompat の Light テーマの定義を実際に見てみましょう。

<!-- Platform-independent theme providing an action bar in a light-themed activity. -->
<style name="Theme.AppCompat.Light" parent="Base.Theme.AppCompat.Light" />

https://github.com/androidx/androidx/blob/907e04af2a88cdf288e132af208d396449186faf/appcompat/appcompat/src/main/res/values/themes.xml#L34-L35

すでにTheme.AppCompat.Light.NoActionBarはご覧いただいた通りです。
ここではその継承元を順に追いかけていきたいと思います。
まずは上記のTheme.AppCompat.Lightが継承元になります。ここではparent属性を使ってBase.Theme.AppCompat.Lightを継承していますから、さらに継承元を追いかけてみます。

<style name="Base.Theme.AppCompat.Light" parent="Base.V7.Theme.AppCompat.Light">
</style>

https://github.com/androidx/androidx/blob/907e04af2a88cdf288e132af208d396449186faf/appcompat/appcompat/src/main/res/values/themes_base.xml#L526-L527

こちらもさらに継承元に遡ります。

<!-- Base platform-dependent theme providing an action bar in a light-themed activity. -->
<style name="Base.V7.Theme.AppCompat.Light" parent="Platform.AppCompat.Light">
    ...
    <!-- Button styles -->
    <item name="buttonStyle">@style/Widget.AppCompat.Button</item>
    <item name="buttonStyleSmall">@style/Widget.AppCompat.Button.Small</item>
    <item name="android:textAppearanceButton">@style/TextAppearance.AppCompat.Widget.Button</item>
    ...
</style>

https://github.com/androidx/androidx/blob/907e04af2a88cdf288e132af208d396449186faf/appcompat/appcompat/src/main/res/values/themes_base.xml#L349-L521

まだ継承元がありますが、Base.V7.Theme.AppCompat.Lightを見るだけでもなんとなく雰囲気は掴めるのではないかと思います。スタイルリソースが実際にテーマ属性として利用されているということが読み取れるのではないでしょうか。

続いては、各スタイルリソースごとの個別の使い方を見ていきたいと思います。

テーマ

テーマは主に AndroidManifest.xml<application> または <activity> タグに android:theme 属性として指定します。
Android Studio で新規にアプリケーションを作成したことがある方はご覧になったことがあるかと思います。

その他の使い方としては、コード上でAlertDialog用のテーマを指定するといった使い方だったり、テーマをよくご存知の方であればContextThemeWrapperを使ってテーマを書き換えて処理を行うようなコードを書かれたことがある方もいらっしゃるかと思います。

スタイル

スタイルの利用は比較的よく見かけるのではないかと思います。
例えばAlertDialogのボタン領域のレイアウトではこのように利用されています。

<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
            android:id="@+id/buttonPanel"
            style="?attr/buttonBarStyle"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:fillViewport="true"
            android:scrollIndicators="top|bottom">

https://github.com/androidx/androidx/blob/8cb282ccdbb00687dbf253a4419ded0dfc786fb5/appcompat/appcompat/src/main/res/layout/abc_alert_dialog_button_bar_material.xml#L18-L24

style属性でテーマ属性の参照を指定しています。このテーマ属性のTheme.AppCompat.Lightテーマにおける実態はこちらになります。

<item name="buttonBarStyle">@style/Widget.AppCompat.ButtonBar</item>

https://github.com/androidx/androidx/blob/907e04af2a88cdf288e132af208d396449186faf/appcompat/appcompat/src/main/res/values/themes_base.xml#L492

このように、個別の View に対してstyle属性を使ってスタイルを指定するという使い方をします。

テーマオーバーレイ

テーマオーバーレイは主に ActionBar や Toolbar と Dialog の実装に使われることが多いです。
また、個別の View に指定して部分的に色を変更するといった用途にも使われます。
テーマオーバーレイの用途については定義箇所に少しだけコメントが記載されていますので、まずはそちらを見てみましょう。

<!-- Theme overlay that replaces colors with their light versions but preserves
     the value of colorAccent, colorPrimary and its variants. -->
<style name="ThemeOverlay.AppCompat.Light" parent="Base.ThemeOverlay.AppCompat.Light" />

https://github.com/androidx/androidx/blob/907e04af2a88cdf288e132af208d396449186faf/appcompat/appcompat/src/main/res/values/themes.xml#L81-L83

ご覧の通り、色を置換しますよというコメントが記載されていますね。
次に実際のテーマオーバーレイの定義を見てみましょう。

<style name="Base.ThemeOverlay.AppCompat.Light" parent="Platform.ThemeOverlay.AppCompat.Light">
    <item name="android:windowBackground">@color/background_material_light</item>
    <item name="android:colorForeground">@color/foreground_material_light</item>
    <item name="android:colorForegroundInverse">@color/foreground_material_dark</item>
    <item name="android:colorBackground">@color/background_material_light</item>
    <item name="android:colorBackgroundCacheHint">@color/abc_background_cache_hint_selector_material_light</item>
    <item name="colorBackgroundFloating">@color/background_floating_material_light</item>
    ...
</style>

https://github.com/androidx/androidx/blob/907e04af2a88cdf288e132af208d396449186faf/appcompat/appcompat/src/main/res/values/themes_base.xml#L671-L698

色に関する定義だけが記述されているということが見て取れますね。
UIデザインとして「通常利用しているテーマとは異なるテーマを部分的に採用してメリハリをつけたい」とか「部分的にユーザーに与える印象をスッキリさせたい」といった要望が出てくることはよくありますので、そうした場合にテーマオーバーレイを使って部分的に色を変えつつもその色の変え方は共通化するといった対応が可能になります。

それでは実際に利用している箇所を見てみましょう。

<androidx.appcompat.widget.Toolbar
    android:id="@+id/toolbar"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="?attr/colorPrimaryDark"
    android:minHeight="?attr/actionBarSize"
    android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
    app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />

https://github.com/androidx/androidx/blob/8cb282ccdbb00687dbf253a4419ded0dfc786fb5/appcompat/appcompat/src/androidTest/res/layout/appcompat_toolbar_activity.xml#L24-L31

こちらはテストコードに含まれているレイアウトリソースになりますが、使い方自体は通常のアプリケーションでも同様です。 android:theme 属性で View のテーマを上書きしています。その下の android:popupTheme は Toolbar 特有の属性で、こちらもテーマオーバーレイでテーマを上書きしています。

ちなみに、 Dialog のテーマオーバーレイでは色以外の属性も指定されており、 Dialog の見た目だけでなく挙動に影響するような設定も含まれています。テーマオーバーレイにはそうした利用方法もありますが、筆者の経験上、一般的なアプリケーションの開発においてそこまでの知識が必要になることはあまり多くはありません。

テキストアピアランス

テキストアピアランスはその名前の通りテキストの見た目を指定するものです。
TextView を継承した View と、一部のテキスト表示をサポートしている View (例えば Material Components for Android の TabLayout など) で利用することができます。

それでは実際に利用されているコードを見てみましょう。

    <TextView
        android:id="@+id/message"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="@dimen/tooltip_margin"
        android:paddingLeft="@dimen/tooltip_horizontal_padding"
        android:paddingStart="@dimen/tooltip_horizontal_padding"
        android:paddingRight="@dimen/tooltip_horizontal_padding"
        android:paddingEnd="@dimen/tooltip_horizontal_padding"
        android:paddingTop="@dimen/tooltip_vertical_padding"
        android:paddingBottom="@dimen/tooltip_vertical_padding"
        android:maxWidth="256dp"
        android:background="?attr/tooltipFrameBackground"
        android:textAppearance="@style/TextAppearance.AppCompat.Tooltip"
        android:textColor="?attr/tooltipForegroundColor"
        android:maxLines="3"
        android:ellipsize="end"
    />

https://github.com/androidx/androidx/blob/8cb282ccdbb00687dbf253a4419ded0dfc786fb5/appcompat/appcompat/src/main/res/layout/abc_tooltip.xml#L23-L40

こちらは Tooltip のレイアウトになります。 android:textAppearance="@style/TextAppearance.AppCompat.Tooltip" となっていますね。このように android:textAppearance 属性で指定してテキストの見た目を設定します。

まとめ

  • スタイルリソースにはいくつか種類があります。
    • テーマ
    • スタイル
    • テーマオーバーレイ
    • テキストアピアランス
  • テーマは主に AndroidManifest.xml<application> あるいは <activity> タグの android:theme 属性で指定します。
  • スタイルは主に View のstyle属性で指定します。
  • テーマオーバーレイは主に View の android:theme 属性(古いプロジェクトでは app:theme 属性)で指定します。
  • テキストアピアランスは主に TextView の android:textAppearance 属性で指定します。
種類 名前 用途
テーマ Theme. Application / Activity のandroid:theme属性に指定
スタイル Widget. View のstyle属性に指定
テーマオーバーレイ ThemeOverlay. View のandroid:theme属性に指定
テキストアピアランス TextApperance. TextView のandroid:textAppearance属性に指定

Discussion