【Android】初心者向け 標準Spinnerを代替するドロップボックス

Android標準のウィジェットSpinnerは便利ですが、ぱっと見で選択できることがユーザーには分かりにくい気がします。（SpinnerのUIカスタマイズ方法を私が知らないだけかも・・・）
Spinnerと同等の機能を持ち、見た目も選択可能なことがひと目でわかるドロップボックスを紹介します。
AutoCompleteTextViewを利用した有名な方法だと思いますが、一通り動作するフルセットのサンプルとしてご利用ください。

#ドロップボックスの外観
![dropbox_image1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/546646/922059df-8bb5-1ffa-653a-967aea612273.png)

#用意したサンプルプログラムの外観
![ebc57c23580f98c75d2088eea08b887d.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/546646/fa513107-83b1-635a-577f-d6cc174bf2c8.gif)

#サンプルの仕様
・ドロップボックスで選択されている値をプログラムから取得できる。
・プログラムからドロップボックスの任意の値を選択状態にできる。
・もちろんユーザーはドロップボックスの任意の値を選択できる。
・ソフトウェアキーボードが表示されていてドロップボックスがフォーカスを受けた場合、邪魔なキーボードを隠す。

#環境
android studio arctic fox | 2020.3.1 patch 4
compileSdk 32

#コード
さっそくコードの主要部分を紹介します。
ここではわかり易さを重視して必要な部分のコードのみ抜粋しています。
ソース全体は https://github.com/masayahak/Dropdown へアップしているので必要な場合は参照してください。

##ドロップボックスのレイアウトファイル
```xml:fragment_dropdown.xml
        <!-- ドロップダウン -->
        <com.google.android.material.textfield.TextInputLayout
            style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
            android:id="@+id/dropdown_label"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:hint="カテゴリー"
            >

            <AutoCompleteTextView
                android:id="@+id/dropdown_item"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:inputType="none"
                android:text="和食"
                tools:ignore="LabelFor" />

        </com.google.android.material.textfield.TextInputLayout>
```
ドロップボックスとして AutoCompleteTextViewを利用しています。
android:inputType="none"とし、キー入力を受け付けないようにしています。


## ドロップボックスのドロップリスト部分のレイアウトファイル
```xml:dropdow_item.xml
<TextView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="TextView"
    android:padding="8dp"
    android:textColor="@color/black"
    android:textStyle="bold"
    android:textSize="16sp"
    />
```
ドロップボックスをクリックし展開される部分の1行分のレイアウトになります。


## ドロップボックスの選択肢を準備する
```xml:array.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string-array name="foodType_array">
        <item>和食</item>
        <item>洋食</item>
        <item>中華</item>
        <item>その他</item>
    </string-array>
</resources>
```

## ドロップボックスへ選択肢のリストをセットする
```kotlin:FragmentDropdown.kt
    // 画面の回転など再描画されたとき用にonCreateViewではなく、onResumeでドロップダウンを初期化する
    override fun onResume() {
        super.onResume()

        // ドロップダウンの初期設定
        val dropdown : AutoCompleteTextView = view?.findViewById(R.id.dropdown_item)!!
        initDropdown(dropdown)

    }

    // ドロップダウンの初期設定
    private fun initDropdown(dropdown: AutoCompleteTextView) {
        // 「R.array.foodType_array」は「values/array.xml」で定義
        val foodTypes = resources.getStringArray(R.array.foodType_array)
        // 「R.layout.dropdown_item」は「vlayout/dropdown_item.xml」で定義
        val arrayAdapter = ArrayAdapter(dropdown.rootView.context, R.layout.dropdown_item, foodTypes)
        dropdown.setAdapter(arrayAdapter)
    }
```

## ドロップボックスへプログラムから値をセットする

![ebc57c23580f98c75d2088eea08b887d.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/546646/d630f849-3264-f345-d87d-a92f3fb37f72.gif)


```kotlin:FragmentDropdown.kt
    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {

        val dropdown : AutoCompleteTextView = view.findViewById(R.id.dropdown_item)

//　　　　　　　　　　　　　　　　　　（中略）

        // ドロップダウンへプログラムから値をセットするサンプル
        val buttonSetDropdown : Button = view.findViewById(R.id.button_set_dropdown)
        buttonSetDropdown.setOnClickListener {
            // サンプルなので固定で「洋食」をセット。
            // 本来はDBからの取得値などで値をセット。
            setDropdownSelection(dropdown, "洋食")
        }
    }

    // 特定の値をドロップダウンにセット
    private fun setDropdownSelection(dropdown: AutoCompleteTextView, item : String) {
        dropdown.setText(item)
        // setTextを実行するとドロップダウンへセットしたアダプタがリセットされるので再設定する
        initDropdown(dropdown)
        // アダプタ再設定時にドロップダウンが開いた状態になるのを防ぐためにフォーカスを外す
        dropdown.clearFocus()
    }
```

## ドロップボックスで選択されている値をプログラムから取得する

![aebf8ad1c2ded2a4a623062c20f77155.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/546646/13536554-69f8-45a9-bc6a-1dc810551394.gif)

```kotlin:FragmentDropdown.kt
    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {

        val dropdown : AutoCompleteTextView = view.findViewById(R.id.dropdown_item)

//　　　　　　　　　　　　　　　　　　（中略）

        // ドロップダウンの現在値をプログラムで取得するサンプル
        val buttonGetDropdown : Button = view.findViewById(R.id.button_get_dropdown)
        buttonGetDropdown.setOnClickListener {
            val textView : TextView = view.findViewById(R.id.dropdown_get_textview)
            textView.text = dropdown.text.toString()
        }

    }

```

#参考
下記の動画を参考にさせていただきました。
参考：Exposed Drop-Down Menu - Forget about Spinner | Android Studio Tutorial
https://www.youtube.com/watch?v=741l_fPKL3Y

#終わりに
非常に短いコードですが、１つずつ調べながら手作りするのは大変ですよね。お役に立てれば幸いです。
コメント、質問も歓迎です。