Android で Spinner の文字を XML だけで中央揃え・右揃えにする方法

# 挨拶と背景

こんにちは。依然として Xamarin + Visual Studio + C# で[賢いお絵描きアプリ](https://github.com/shiatsumat/tegaki-hack)を作ろうとして苦戦している shiatsumat です。[前回の記事](http://qiita.com/shiatsumat/items/881e0f91557b9c2a751d)の執筆から数時間が経ち、細かい修正を重ねているのですが、色の選択の UI も結構良くなってきました。

<img width="243" alt="a.PNG" src="https://qiita-image-store.s3.amazonaws.com/0/31593/7277e138-4ad7-4a41-63ca-27caaba1f929.png">

しかし、Spinner の文字が左揃えなのがどうも気持ち悪いので、中央揃えに変えたくなったのですが、ネットで調べてもプログラムで ArrayAdapter を利用する方法しか見つかりません。このくらい XML で済ませたいものです。ということで試行錯誤し、なんとか方法を見つけました。以下、試行錯誤の過程を書きます。方法を今すぐに知りたい人は[まとめ](http://qiita.com/shiatsumat/items/274fc4261928799b6aea#%E3%81%BE%E3%81%A8%E3%82%81)に飛んでください。

なお、開発環境は Xamarin + Visual Studio で、以下その前提で書いていきますが、Android Studio + Java などにも応用が利くはずです。右揃えにする方法もメモしておきます。

# カスタムテーマを使ってみるが微妙にバグる

調べるうちにどうやらカスタムテーマを使うと上手く行きそうだったので、重い腰を上げてカスタムテーマを使ってみました。

ちょうどクラスを継承するように、parent のテーマを拡張できます。このカスタムテーマは @style/CustomTheme という名前で参照できます。

```xml:Resources/values/styles.xml
<?xml version="1.0" encoding="UTF-8" ?>
<resources>

  <style name="CustomTheme" parent="android:Theme.Holo.Light.NoActionBar">
    <item name="android:spinnerItemStyle">@style/spinnerItemStyle</item>
    <item name="android:spinnerDropDownItemStyle">@style/spinnerDropDownItemStyle</item>
  </style>
  <style name="spinnerItemStyle">
    <item name="android:gravity">center</item>
    <item name="android:textColor">#FF0000</item>
  </style>
  <style name="spinnerDropDownItemStyle">
    <item name="android:gravity">center</item>
    <item name="android:textColor">#00FF00</item>
  </style>

</resources>
```

どうやら spinnerItemStyle と spinnerDropDownItemStyle を設定すると良いみたいなので、そうしてみました。

CustomTheme の item で設定している @style/spinnerItemStyle と @style/spinnerDropDownItemStyle はそれぞれ直後の \<style name="spinnerItemStyle"\> と \<style name="spinnerDropDownItemStyle"\> を参照しています。（item 内に直接 style を書くことはできないようです。）

ただ gravity を center にするだけでも構わないのですが、わかりやすくするために spinnerItemStyle に赤色を、spinnerDropDownItemStyle に緑色を付けてみました。

そして、Activity の Theme を @style/CustomTheme に設定します。さて、どうなるでしょうか。

<img width="243" alt="b.PNG" src="https://qiita-image-store.s3.amazonaws.com/0/31593/60ce4ad2-ff7b-f110-e667-b61063d07366.png">

おお、見事に色が変わっていますね！でも、中央揃えは spinnerDropDownItemStyle の方は上手く行っていますが spinnerItemStyle の方は左揃えのままです。どうしたものでしょうか。（ここで android:textAlignment を center にするなどしてもダメでした。）

# spinnerStyle をいじってみるが奇妙にバグる

どうやら spinnerItemStyle を設定するのでは上手く行かないようなので、なにか他に spinner 系の属性は無いかな、と思って当てずっぽう spinnerStyle と書いてみたら見事ビルドが通りました。なんとなくでわかる名前って素晴らしいですね！（なお、存在しない属性を書くとビルドが通りません。）

```xml:Resources/values/styles.xml
<?xml version="1.0" encoding="UTF-8" ?>
<resources>

  <style name="CustomTheme" parent="android:Theme.Holo.Light.NoActionBar">
    <item name="android:spinnerStyle">@style/spinnerStyle</item>
    <item name="android:spinnerDropDownItemStyle">@style/spinnerDropDownItemStyle</item>
  </style>
  <style name="spinnerStyle">
    <item name="android:gravity">center</item>
  </style>
  <style name="spinnerDropDownItemStyle">
    <item name="android:gravity">center</item>
  </style>

</resources>
```

さて今度はどうでしょう？

<img width="243" alt="c.PNG" src="https://qiita-image-store.s3.amazonaws.com/0/31593/f84f7e08-3c56-d3a1-65dd-4eda276493fb.png">

中央揃えになった！……と思ったらなんとドロップダウンリストが無くなってただのテキストに変わってしまいました！これでは困ります。

# spinnerStyle にも parent を設定してみる

spinnerStyle の設定を下手に上書きしてしまったのが良くないようなので、parent をここでも使って元々の設定を活かしたら上手く行きそうです。ついでに spinnerDropDownItemStyle にも parent を設定しましょう。

```xml:Resources/values/styles.xml
<?xml version="1.0" encoding="UTF-8" ?>
<resources>

  <style name="CustomTheme" parent="android:Theme.Holo.Light.NoActionBar">
    <item name="android:spinnerStyle">@style/spinnerStyle</item>
    <item name="android:spinnerDropDownItemStyle">@style/spinnerDropDownItemStyle</item>
  </style>
  <style name="spinnerStyle" parent="android:Widget.Holo.Light.Spinner">
    <item name="android:gravity">center</item>
  </style>
  <style name="spinnerDropDownItemStyle" parent="android:Widget.Holo.Light.DropDownItem.Spinner">
    <item name="android:gravity">center</item>
  </style>

</resources>
```

さて、どうなるでしょうか……？

<img width="242" alt="d.PNG" src="https://qiita-image-store.s3.amazonaws.com/0/31593/1cd24adb-dfba-4de5-4583-c0d6521e61f5.png">

上手く行きました！！！

# 右揃えのカスタムテーマを部分的に適用する

利便性のため、Spinner の文字を右揃えにする方法と部分的にカスタムテーマを適用する方法をまとめて説明しておこうと思います。

右揃え用のカスタムテーマは center を center_vertical|right に変えることで作れます。

```xml:Resources/values/styles.xml
<?xml version="1.0" encoding="UTF-8" ?>
<resources>

  <style name="CustomTheme" parent="android:Theme.Holo.Light.NoActionBar">
    <item name="android:spinnerStyle">@style/spinnerStyle</item>
    <item name="android:spinnerDropDownItemStyle">@style/spinnerDropDownItemStyle</item>
  </style>
  <style name="spinnerStyle" parent="android:Widget.Holo.Light.Spinner">
    <item name="android:gravity">center</item>
  </style>
  <style name="spinnerDropDownItemStyle" parent="android:Widget.Holo.Light.DropDownItem.Spinner">
    <item name="android:gravity">center</item>
  </style>

  <style name="CustomTheme.RightAlign" parent="CustomTheme">
    <item name="android:spinnerStyle">@style/spinnerStyle.RightAlign</item>
    <item name="android:spinnerDropDownItemStyle">@style/spinnerDropDownItemStyle.RightAlign</item>
  </style>
  <style name="spinnerStyle.RightAlign" parent="spinnerStyle">
    <item name="android:gravity">center_vertical|right</item>
  </style>
  <style name="spinnerDropDownItemStyle.RightAlign" parent="spinnerDropDownItemStyle">
    <item name="android:gravity">center_vertical|right</item>
  </style>

</resources>
```

部分的にテーマを適用する方法は簡単で、該当箇所の android:theme を設定するだけです。Linejoin（頂点のスタイル）の部分だけに右揃えを適用しました。

```xml:Resources/layout/PaintDialog.axml
（前略）
            <TextView
                android:text="@string/Linejoin"
                android:textSize="20dp"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center" />
            <Spinner
                android:id="@+id/Linejoin"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:layout_marginTop="5dp"
                android:theme="@style/CustomTheme.RightAlign"
                android:entries="@array/Linejoins" />
（後略）
```
<img width="244" alt="e.PNG" src="https://qiita-image-store.s3.amazonaws.com/0/31593/781043df-bc0d-0e66-1ce9-c47640eb201f.png">

上手く行きました！

# まとめ

以上、ポイントは

* 元々のテーマを parent としてカスタムテーマを作る
* 全体に適用したければ Activity のテーマを設定すればよい
* 特定箇所だけスタイルを適用するにはその部分の android:theme を設定する
* ドロップダウンで表示される項目については spinnerDropDownItemStyle で \<item name="android:gravity"\>center\</item\> を使えば中央揃えにできる
* center を center_vertical|right にすれば右揃えになる
* メインで表示される項目については spinnerItemStyle でスタイルを設定できるが、中央揃えにするには spinnerStyle のスタイルを設定する必要がある
* なおかつ spinnerStyle の parent を適切に指定する必要がある

の7点です。意外と難しいですね……

もう一度中央揃えのためのソースコードを貼っておきます。parent の部分さえ調整すればコピペで使えるはずです。

```xml:Resources/values/styles.xml
<?xml version="1.0" encoding="UTF-8" ?>
<resources>

  <style name="CustomTheme" parent="android:Theme.Holo.Light.NoActionBar">
    <item name="android:spinnerStyle">@style/spinnerStyle</item>
    <item name="android:spinnerDropDownItemStyle">@style/spinnerDropDownItemStyle</item>
  </style>
  <style name="spinnerStyle" parent="android:Widget.Holo.Light.Spinner">
    <item name="android:gravity">center</item>
  </style>
  <style name="spinnerDropDownItemStyle" parent="android:Widget.Holo.Light.DropDownItem.Spinner">
    <item name="android:gravity">center</item>
  </style>

</resources>
```

カスタムテーマを使うテクニックはほかにも色々と応用が利きそうです。

さっき見つけたのですが、Android の開発者用リファレンスの [R.attr](https://developer.android.com/reference/android/R.attr.html) と [R.style](https://developer.android.com/reference/android/R.style.html) に Android で用意されている属性とスタイルの一覧があるので、これを使えば当てずっぽうをしなくても済みますね。

# あとがき

アプリ開発はちょっとしたところでつまずいて、断片的な知識をネットからかき集めて、仕組みを理解して、出来ることが一気に増えて、またつまずいて、ということの繰り返しで、妙な中毒性があって楽しいです。また何か発見があれば書こうと思います。よろしくお願いします。