techium

このブログは何かに追われないと頑張れない人たちが週一更新をノルマに技術情報を発信するブログです。もし何か調査して欲しい内容がありましたら、@kobashinG or @muchiki0226 までいただけますと気が向いたら調査するかもしれません。

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);
    }

スクリーンショット

ここで問題が発生しました。
ドロップダウンでは文字が全て大文字ですが、選択済みのスクリーンショットでは先頭だけ大文字になっています。
f:id:forestsoftjpdev:20160816204921p:plain:w300 f:id:forestsoftjpdev:20160816204927p:plain:w300

原因を探る

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;
            }
        };

f:id:forestsoftjpdev:20160816211226p:plain:w300 f:id:forestsoftjpdev:20160816211231p:plain:w300

このようにドロップダウンと選択後の表示共に、意図した通り表示されました。 Spinnerを実装する場合は、AdapterのgetDropDownViewもオーバーライドしないといけないということでした。

以上です。