スタイルとテーマ

Compose をお試しください
Jetpack Compose は、Android で推奨される UI ツールキットです。Compose でテーマ設定を操作する方法について学習します。

Android のスタイルとテーマを使用すると、ウェブデザインのスタイルシートと同様に、アプリのデザインの詳細を UI の構造や動作から分離できます。

スタイルとは、単一の View の外観を指定する属性の集まりです。スタイルでは、フォントの色、フォントサイズ、背景色などの属性を指定できます。

テーマは、個々のビューだけでなく、アプリ、アクティビティ、ビュー階層全体に適用される属性のコレクションです。テーマを適用すると、アプリまたはアクティビティ内のすべてのビューに、サポートされているテーマの各属性が適用されます。テーマは、ステータスバーやウィンドウ背景などのビュー以外の要素にもスタイルを適用できます。

スタイルとテーマは、res/values/ のスタイル リソース ファイル(通常は styles.xml という名前)で宣言します。

図 1. 同じアクティビティに適用された 2 つのテーマ: Theme.AppCompat(左)と Theme.AppCompat.Light(右)。

テーマとスタイルの違い

テーマとスタイルには多くの類似点がありますが、使用目的が異なります。テーマとスタイルは、属性をリソースにマッピングする Key-Value ペアという同じ基本構造を持っています。

スタイルは、特定のタイプのビューの属性を指定します。たとえば、あるスタイルでボタンの属性を指定し、スタイルで指定するすべての属性は、レイアウト ファイルで設定できる属性です。すべての属性をスタイルに抽出すると、複数のウィジェットで簡単に使用、管理できるようになります。

テーマは、スタイル、レイアウト、ウィジェットなどから参照できる名前付きリソースのコレクションを定義します。テーマは、colorPrimary などのセマンティック名を Android リソースに割り当てます。

スタイルとテーマは連携して動作するように作られています。たとえば、ボタンの一部を colorPrimary、別の部分を colorSecondary に指定するスタイルがあるとします。これらの色の実際の定義はテーマで指定されています。デバイスが夜間モードに切り替わると、アプリは「ライト」テーマから「ダーク」テーマに切り替わり、それらのリソース名の値がすべて変更されます。スタイルは特定の色の定義ではなくセマンティック名を使用しているため、スタイルを変更する必要はありません。

テーマとスタイルがどのように連携するかについて詳しくは、ブログ投稿 Android のスタイル設定: テーマとスタイルをご覧ください。

スタイルを作成して適用する

新しいスタイルを作成するには、プロジェクトの res/values/styles.xml ファイルを開きます。作成するスタイルごとに、次の操作を行います。

  1. スタイルを一意に識別する名前を持つ <style> 要素を追加します。
  2. 定義するスタイル属性ごとに <item> 要素を追加します。各アイテムの name は、レイアウトで XML 属性として使用する属性を指定します。<item> 要素の値は、その属性の値です。

たとえば、次のようなスタイルを定義するとします。

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <style name="GreenText" parent="TextAppearance.AppCompat">
        <item name="android:textColor">#00FF00</item>
    </style>
</resources>

次のようにスタイルをビューに適用できます。

<TextView
    style="@style/GreenText"
    ... />

スタイルで指定された各属性は、ビューが受け入れる場合にそのビューに適用されます。ビューは、受け入れない属性を無視します。

ただし、通常は、個々のビューにスタイルを適用するのではなく、このガイドの別のセクションで説明するように、アプリ全体、アクティビティ、またはビューのコレクションにテーマとしてスタイルを適用します。

スタイルを拡張しカスタマイズする

独自のスタイルを作成する場合は、プラットフォームの UI スタイルとの互換性を維持するために、常にフレームワークまたはサポート ライブラリの既存のスタイルを拡張します。スタイルを拡張するには、拡張するスタイルを parent 属性で指定します。継承されたスタイル属性をオーバーライドして、新しい属性を追加できます。

たとえば、Android プラットフォームのデフォルトのテキストの外観を継承し、次のように変更できます。

<style name="GreenText" parent="@android:style/TextAppearance">
    <item name="android:textColor">#00FF00</item>
</style>

ただし、コアアプリのスタイルは常に Android サポート ライブラリから継承してください。サポート ライブラリのスタイルは、各バージョンで使用可能な UI 属性に合わせて各スタイルを最適化することで、互換性を実現しています。サポート ライブラリのスタイルは、プラットフォームのスタイルと似た名前であることがよくありますが、AppCompat が含まれています。

ライブラリまたは独自のプロジェクトからスタイルを継承するには、前の例に示した @android:style/ 部分なしで親スタイル名を宣言します。たとえば、次の例では、サポート ライブラリからテキストの外観スタイルを継承しています。

<style name="GreenText" parent="TextAppearance.AppCompat">
    <item name="android:textColor">#00FF00</item>
</style>

parent 属性を使用する代わりに、ドット表記でスタイル名を拡張することで、スタイルを継承することもできます(プラットフォームのスタイルを除く)。つまり、継承するスタイルの名前を、ピリオドで区切ってスタイルの名前の前に付けます。通常、この操作は、他のライブラリのスタイルではなく、独自のスタイルを拡張する場合にのみ行います。たとえば、次のスタイルは、前の例の GreenText からすべてのスタイルを継承し、テキストサイズを大きくします。

<style name="GreenText.Large">
    <item name="android:textSize">22dp</item>
</style>

このように、名前を連結することで、スタイルを何度でも継承できます。

<item> タグで宣言できる属性を確認するには、さまざまなクラス リファレンスの「XML 属性」の表をご覧ください。すべてのビューは、ベースの View クラスの XML 属性をサポートしています。また、多くのビューは独自の特別な属性を追加しています。たとえば、TextView XML 属性には、EditText ウィジェットなど、入力を受け取るテキストビューに適用できる android:inputType 属性が含まれています。

スタイルをテーマとして適用する

テーマは、スタイルの作成と同じ方法で作成できます。違いは、適用方法にあります。ビューに style 属性を使用してスタイルを適用するのではなく、AndroidManifest.xml ファイル内の <application> タグまたは <activity> タグに android:theme 属性を使用してテーマを適用します。

たとえば、Android サポート ライブラリのマテリアル デザインの「ダーク」テーマをアプリ全体に適用するには、次のようにします。

<manifest ... >
    <application android:theme="@style/Theme.AppCompat" ... >
    </application>
</manifest>

また、「ライト」テーマを 1 つのアクティビティのみに適用する方法は次のとおりです。

<manifest ... >
    <application ... >
        <activity android:theme="@style/Theme.AppCompat.Light" ... >
        </activity>
    </application>
</manifest>

アプリまたはアクティビティのすべてのビューは、指定されたテーマで定義されているスタイルから、サポートされているスタイルを適用します。ビューがスタイルで宣言されている属性の一部のみをサポートしている場合、ビューはそれらの属性のみを適用し、サポートしていない属性は無視します。

Android 5.0(API レベル 21)および Android サポート ライブラリ v22.1 以降では、レイアウト ファイル内のビューに android:theme 属性を指定することもできます。これにより、そのビューと子ビューのテーマが変更されます。これは、インターフェースの特定の部分でテーマのカラーパレットを変更する場合に便利です。

上記の例は、Android サポート ライブラリで提供されている Theme.AppCompat などのテーマを適用する方法を示しています。ただし、通常はアプリのブランドに合わせてテーマをカスタマイズする必要があります。これを行うには、次のセクションで説明するように、サポート ライブラリからこれらのスタイルを拡張し、一部の属性をオーバーライドするのが最善の方法です。

スタイル階層

Android には、Android アプリ全体で属性を設定するさまざまな方法が用意されています。たとえば、レイアウトで属性を直接設定したり、ビューにスタイルを適用したり、レイアウトにテーマを適用したり、プログラムで属性を設定したりできます。

アプリのスタイル設定方法を選択するときは、Android のスタイル階層に注意してください。一般的には、一貫性を保つために、できるだけテーマとスタイルを使用します。複数の場所で同じ属性を指定した場合、最終的に適用される属性は次のリストによって決まります。リストは優先度の高い順に並べられています。

  1. テキストスパンを使用して、TextView 派生クラスに文字レベルまたは段落レベルのスタイルを適用する。
  2. プログラムによる属性の適用。
  3. 個々の属性をビューに直接適用する。
  4. ビューにスタイルを適用する。
  5. デフォルトのスタイル設定。
  6. ビューのコレクション、アクティビティ、またはアプリ全体にテーマを適用する。
  7. 特定のビュー固有のスタイルを適用する(TextView に TextAppearance を設定するなど)。

図 2. span によるスタイル設定は textAppearance によるスタイル設定をオーバーライドします。

TextAppearance

スタイルに関する制限として、View にはスタイルを 1 つしか適用できません。ただし、TextView では、次の例に示すように、スタイルと同様に機能する TextAppearance 属性を指定することもできます。

<TextView
    ...
    android:textAppearance="@android:style/TextAppearance.Material.Headline"
    android:text="This text is styled via textAppearance!" />

TextAppearance を使用すると、テキスト固有のスタイル設定を定義しながら、View のスタイルを他の用途に使用できます。ただし、View またはスタイルでテキスト属性を直接定義すると、それらの値が TextAppearance の値をオーバーライドします。

TextAppearance は、TextView が提供するスタイル設定属性のサブセットをサポートしています。属性の完全なリストについては、TextAppearance をご覧ください。

含まれていない一般的な TextView 属性には、lineHeight[Multiplier|Extra]、lines、breakStrategy、hyphenationFrequency があります。TextAppearance は段落レベルではなく文字レベルで機能するため、レイアウト全体に影響する属性はサポートされていません。

デフォルトのテーマをカスタマイズする

Android Studio でプロジェクトを作成すると、プロジェクトの styles.xml ファイルで定義されているように、デフォルトでマテリアル デザインのテーマがアプリに適用されます。この AppTheme スタイルは、サポート ライブラリのテーマを拡張したもので、アプリバーやフローティング アクション ボタン(使用されている場合)など、主要な UI 要素で使用される色属性のオーバーライドが含まれています。そのため、提供された色を更新することで、アプリのカラーデザインをすばやくカスタマイズできます。

たとえば、styles.xml ファイルは次のようになります。

<style name="AppTheme" 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>

スタイル値は、実際には、プロジェクトの res/values/colors.xml ファイルで定義されている他のカラーリソースへの参照です。このファイルを編集して色を変更します。 ダイナミック カラーと追加のカスタムカラーを使用してユーザー エクスペリエンスを向上させる方法については、マテリアル デザインの色の概要をご覧ください。

色を確認したら、res/values/colors.xml の値を更新します。

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!--   Color for the app bar and other primary UI elements. -->
    <color name="colorPrimary">#3F51B5</color>

    <!--   A darker variant of the primary color, used for
           the status bar (on Android 5.0+) and contextual app bars. -->
    <color name="colorPrimaryDark">#303F9F</color>

    <!--   a secondary color for controls like checkboxes and text fields. -->
    <color name="colorAccent">#FF4081</color>
</resources>

その後、必要に応じて他のスタイルをオーバーライドできます。たとえば、アクティビティの背景色を次のように変更できます。

<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    ...
    <item name="android:windowBackground">@color/activityBackground</item>
</style>

テーマで使用できる属性の一覧については、R.styleable.Theme の属性の表をご覧ください。レイアウト内のビューのスタイルを追加する際、ビュークラス リファレンスの「XML 属性」テーブルを参照して属性を見つけることもできます。たとえば、すべてのビューは、基本 View クラスの XML 属性をサポートしています。

ほとんどの属性は特定のタイプのビューに適用されますが、すべてのビューに適用される属性もあります。ただし、R.styleable.Theme に記載されているテーマ属性の一部は、レイアウト内のビューではなく、アクティビティ ウィンドウに適用されます。たとえば、windowBackground はウィンドウの背景を変更し、windowEnterTransition はアクティビティの開始時に使用する遷移アニメーションを定義します。詳細については、アニメーションを使用してアクティビティを起動するをご覧ください。

Android サポート ライブラリには、Theme.AppCompat から拡張されたテーマをカスタマイズするために使用できる他の属性も用意されています。たとえば、上記の例に示した colorPrimary 属性などです。これらは、ライブラリの attrs.xml ファイルで確認することをおすすめします。

サポート ライブラリには、上記の例に示したものとは異なるテーマも用意されています。利用可能なテーマを確認するには、ライブラリの themes.xml ファイルを参照することをおすすめします。

バージョン固有のスタイルを追加する

新しいバージョンの Android で使用したいテーマ属性が追加された場合は、古いバージョンとの互換性を維持しながら、それらの属性をテーマに追加できます。必要なのは、リソース バージョン修飾子を含む values ディレクトリに保存された別の styles.xml ファイルだけです。

res/values/styles.xml        # themes for all versions
res/values-v21/styles.xml    # themes for API level 21+ only

values/styles.xml ファイル内のスタイルはすべてのバージョンで使用できるため、values-v21/styles.xml 内のテーマはそれらを継承できます。つまり、「ベース」テーマから始めて、バージョン固有のスタイルで拡張することで、スタイルの重複を回避できます。

たとえば、Android 5.0(API レベル 21)以降のウィンドウ遷移を宣言するには、新しい属性を使用する必要があります。res/values/styles.xml の基本テーマは次のようになります。

<resources>
    <!-- Base set of styles that apply to all versions. -->
    <style name="BaseAppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <item name="colorPrimary">@color/primaryColor</item>
        <item name="colorPrimaryDark">@color/primaryTextColor</item>
        <item name="colorAccent">@color/secondaryColor</item>
    </style>

    <!-- Declare the theme name that's actually applied in the manifest file. -->
    <style name="AppTheme" parent="BaseAppTheme" />
</resources>

次に、次のように res/values-v21/styles.xml にバージョン固有のスタイルを追加します。

<resources>
    <!-- extend the base theme to add styles available only with API level 21+ -->
    <style name="AppTheme" parent="BaseAppTheme">
        <item name="android:windowActivityTransitions">true</item>
        <item name="android:windowEnterTransition">@android:transition/slide_right</item>
        <item name="android:windowExitTransition">@android:transition/slide_left</item>
    </style>
</resources>

これで、マニフェスト ファイルに AppTheme を適用できるようになり、システム バージョンごとに利用可能なスタイルが選択されます。

さまざまなデバイスで代替リソースを使用する方法については、代替リソースを提供するをご覧ください。

ウィジェット スタイルをカスタマイズする

フレームワークとサポート ライブラリのすべてのウィジェットには、デフォルトのスタイルがあります。たとえば、サポート ライブラリのテーマを使用してアプリのスタイルを設定すると、Button のインスタンスは Widget.AppCompat.Button スタイルを使用してスタイル設定されます。ボタンに別のウィジェット スタイルを適用する場合は、レイアウト ファイルの style 属性を使用します。たとえば、次のコードはライブラリの枠線なしボタンのスタイルを適用します。

<Button
    style="@style/Widget.AppCompat.Button.Borderless"
    ... />

このスタイルをすべてのボタンに適用する場合は、次のようにテーマの buttonStyle で宣言します。

<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    <item name="buttonStyle">@style/Widget.AppCompat.Button.Borderless</item>
    ...
</style>

他のスタイルを拡張するのと同様に、ウィジェット スタイルを拡張して、カスタム ウィジェット スタイルをレイアウトまたはテーマに適用することもできます。

参考情報

テーマとスタイルの詳細については、以下の参考情報をご覧ください。

ブログ投稿