つぶしの効く Android のスタイルテクニック

Android アプリ全体で統一したデザインスタイルがあると、つぶしが効いて良いですよね。
そのための仕組みとして、Android では`テーマ`というものが設けられています。`テーマ`を用いることで、アプリケーション全体の配色を決めたり、文字のスタイルを決めたりすることができるようになります。

一つの`テーマ`を全体に適用するだけでよければそれはそれで良いのですが、実際問題としては複数の`テーマ`を画面ごとに使い分けることもよくある話かと思います。この画面はフルスクリーンで `ActionBar`を消すけど、別の画面では`ActionBar`を出す、みたいな。

さて、複数の`テーマ`を使う場合、たとえば、Light と Dark でボタンの色みを変えたい、という事が起こります。色合いがよく似ているならともかく、Light と Dark のように大きな違いがある場合は、色の調整をしたほうが良いでしょう。

また、`テーマ`は各種の`スタイル`を統合するための仕組みでもあります。`Button`の見た目、`TextView`の文字のスタイルなど、目的別に`スタイル`を切り出すことによって、それぞれの`スタイル`に命名をすることができます。`テーマ`ごとにその`スタイル`を切り替えられるようにしておけば、修正も楽になることでしょう。

## Theme で使える属性値

[このファイル](https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/core/res/res/values/themes.xml)に、デフォルトで定義されている属性値がリストアップされています。というか、その XML そのものです。
おびただしい量の属性値があるので、そのすべてを使いこなすのは至難の業かもしれませんが、例えばボタンの`スタイル`を変えたければ、自分の`styles.xml`にある`テーマ`に、以下の行を追加すればよいのです。

```xml

<resources>
  <style name="MyTheme" parent="android:Theme.Light">
    <item name="android:buttonStyle">@style/MyButtonStyle</item>
  </style>

  <style name="MyButtonStyle" parent="android:Widget.Button">
  </style>
</resources>
```

これを、`テーマ`ごとに使い分けるので

```xml

<resources>
  <style name="MyTheme" parent="android:Theme.Light">
    <item name="android:buttonStyle">@style/MyLightButtonStyle</item>
  </style>
  <style name="MyAnotherTheme" parent="android:Theme">
    <item name="android:buttonStyle">@style/MyDarkButtonStyle</item>
  </style>

  <style name="MyLightButtonStyle" parent="android:Widget.Button">
  </style>
  <style name="MyDarkButtonStyle" parent="android:Widget.Button">
  </style>
</resources>
```

となります。これで、すべてのボタンが`テーマ`によって指定された`スタイル`になるので、ボタンごとに`style`属性値を設定する必要がなくなりました。

## 属性値の解決を遅延させる

この表現が正しいのかよく分かりませんが、直接属性値に`スタイル`や`drawable`や`string`などへの参照を設定するのではなく、一旦別の箱を用意してそれを参照し、その箱の実態はテーマかどこかで決めるような、ちょっとまどろっこしいですがそんなやり方があります。

```xml:attrs.xml

<resources>
  <attr name="my_drawable" format="reference"/>
</resources>
```

を用意しすると、`my_drawable`という独自の属性が定義されます。これを用いると、

```xml:styles.xml

<resources>
  <style name="MyTheme" parent="android:Theme.Light">
    <item name="my_drawable">@drawable/my_icon_1</item>
  </style>
  <style name="MyAnotherTheme" parent="android:Theme">
    <item name="my_drawable">@drawable/my_icon_2</item>
  </style>
</resources>
```

と言うように宣言でき、レイアウト上では、

```xml:someone.xml

<ImageView
  ...
  src="?attr/my_drawable"
  .../>
```

のように、`?attr/属性名`と表記することで、`テーマ`によって画像が切り替えられるようになります。これを`スタイル`にまとめる属性に応用すれば、Android Framework の`テーマ`がやっていることと同じことを、自分でガシガシ作りこんでいくことができます。

## 継承

`テーマ`と`スタイル`には継承の考え方があります。
その方法には2種類あり、ひとつには`parent`属性を用いる方法、もうひとつには、`.`区切りの命名規則による方法です。

`parent`属性は簡単で、親にしたい`テーマ`や`スタイル`を属性値とすればよいだけです。
ただし、公式のリファレンスに曰く、この方法は「フレームワークのリソースを親として参照する場合に限る」と。

しかし、自分で用意した`スタイル`や`テーマ`も継承したい場面があるでしょう。そこで`.`区切りの命名規則を用います。

```xml:styles.xml

<resources>
  <style name="MyLightButtonStyle" parent="android:Widget.Button">
  </style>
  <style name="MyLightButtonStyle.Hoge">
  </style>
</resources>
```

以上のように作ると、`MyLightButtonStyle`の属性を`MyLightButtonStyle.Hoge`が引き継ぎ、かつ`MyLightButtonStyle.Hoge`独自の拡張ができるようになります。名前空間を区切る間隔で継承による属性値の引き継ぎができるので、スタイルの整理をする際によく用いられます。

## まとめ

同じ画面でのテーマの切り替えがそれほど頻繁にあるかどうかはアプリの仕様に依存することと思いますが、往々にしてとっ散らかって収集がつかなくなりがちなリソースも、これらのテクニックでうまく統合できるようになると、管理コストが減らせてよいですね。