Spinnerのデータとしてenumの配列を使用する場合のTIPS
Spinnerの表示データとしてenumを使用したときに引っかかった罠があったので共有したいと思います。
Spinnerとは
Spinnerは複数の選択肢から一つの要素を選択するためのUIを提供するWidgetです。 詳しいことはDeveloperサイトを御覧ください。 developer.android.com
基本的な実装
ActivityにSpinnerとTextViewを配置し、Spinnerの選択状況によってTextViewのテキストと色が変わるものを実装します。
レイアウト
<?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"> <RelativeLayout android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin"> <Spinner android:id="@+id/spinner" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentTop="true" android:layout_centerHorizontal="true"/> <TextView android:id="@+id/textView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/spinner" android:layout_centerHorizontal="true" android:layout_marginTop="@dimen/activity_vertical_margin" tools:text="text"/> </RelativeLayout> </layout>
enum
public enum Colors { RED(R.string.color_red, Color.RED), GREEN(R.string.color_green, Color.GREEN), BLUE(R.string.color_blue, Color.BLUE); public final int stringRes; public final int color; Colors(int stringRes, int color) { this.stringRes = stringRes; this.color = color; } }
Activity
private ActivityMainBinding mBinding; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mBinding = DataBindingUtil.setContentView(this, R.layout.activity_main); ArrayAdapter<Colors> adapter = new ArrayAdapter<Colors>(this, android.R.layout.simple_spinner_item, Colors.values()) { public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) { convertView = super.getView(position, convertView, parent); } TextView text = (TextView) convertView; text.setText(getItem(position).stringRes); return convertView; } }; adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); Spinner spinner = mBinding.spinner; spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> adapterView, View view, int position, long id) { TextView text = mBinding.textView; Colors selected = Colors.values()[position]; text.setText(getString(R.string.text_format, getString(selected.stringRes))); text.setTextColor(selected.color); } @Override public void onNothingSelected(AdapterView<?> adapterView) { // Nop } }); spinner.setAdapter(adapter); }
スクリーンショット
ここで問題が発生しました。
ドロップダウンでは文字が全て大文字ですが、選択済みのスクリーンショットでは先頭だけ大文字になっています。
原因を探る
Spinnerのソースコードを確認する
android.widget.Spinnerクラスのソースコードを確認すると、インナークラスのDropDownAdapterが使用されているようです。
DropDownの表示はSpinnerにセットされたAdapterクラスのgetDropDownView()が呼ばれていることがわかります。
使用したのはArrayAdapterなので、ソースを確認するとgetDropDownView()からcreateViewFromResource()というメソッドがコールされ、その中で、セットされたArrayデータのtoString()がテキストとしてセットされていることがわかりました。
つまり、Activityで実装したArrayAdapterにてgetDropDownView()をオーバーライドすれば良いということになります。
Adapterのコードを修正します。
ArrayAdapter<Colors> adapter = new ArrayAdapter<Colors>(this, android.R.layout.simple_spinner_item, Colors.values()) { public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) { convertView = super.getView(position, convertView, parent); } TextView text = (TextView) convertView; text.setText(getItem(position).stringRes); return convertView; } // 以下追加したコード public View getDropDownView(int position, View convertView, ViewGroup parent) { if (convertView == null) { convertView = super.getDropDownView(position, convertView, parent); } TextView text = (TextView) convertView; text.setText(getItem(position).stringRes); return convertView; } };
このようにドロップダウンと選択後の表示共に、意図した通り表示されました。 Spinnerを実装する場合は、AdapterのgetDropDownViewもオーバーライドしないといけないということでした。
以上です。