AutoCompleteTextViewをSpinnerとして使うには

TextInputLayoutでドロップダウンメニューを使いたい場合はSpinnerが使えないのでAutoCompleteTextViewをカスタムしてみました。

目次

DropDownNonFilter

フィルタリングせずに常時表示させるためだけのフィルター

class DropDownNonFilter : Filter() {

    override fun performFiltering(constraint: CharSequence?): FilterResults = FilterResults()
        .also { results ->
        // 常にリストがあることが分かれば良い
        results.count = 1
    }

    override fun publishResults(constraint: CharSequence?, results: FilterResults?) {
    }
}

DropDownArrayAdapter

リストの増減は不要なので、フィルターをセットするだけ

class DropDownArrayAdapter(context: Context, resource: Int, items: List<String>) : ArrayAdapter<String>(context, resource, items) {

    override fun getFilter(): Filter = DropDownNonFilter()
}

DropDownTextView

class DropDownTextView(context: Context, attrs: AttributeSet) : AppCompatAutoCompleteTextView(context, attrs) {

    private var isDropDownShowing: Boolean = false

    // 選択肢が多くてスクロールする場合は、この行が一番上に表示される
    // チェック状態にしておくには、アダプタをカスタムしないと無理だと思う
    var selectionPosition: Int = 0

    init {
        // 入力を防止
        inputType = InputType.TYPE_NULL

        // 画面タッチでフォーカスを残さない
        isFocusableInTouchMode = false

        setOnTouchListener { _, ev ->
            when (ev.action) {
                MotionEvent.ACTION_DOWN -> showDropDown()
            }
            false
        }
        setOnDismissListener {
            // ダブルクリックでは開き直さずに閉じたままにしておく
            postDelayed({
                isDropDownShowing = false
            }, 10)
        }
    }

    override fun showDropDown() {
        if (!isDropDownShowing) {
            super.showDropDown()
            listSelection = selectionPosition
            isDropDownShowing = true
        }
    }

    // クリックされたら常時表示
    override fun enoughToFilter(): Boolean = true
}

使ってみる

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    val items = listOf("海", "山", "川")
    val adapter = DropDownArrayAdapter(this, android.R.layout.simple_spinner_dropdown_item, items)
    dropDownTextView.setAdapter(adapter)
    dropDownTextView.setText("川")
    dropDownTextView.selectionPosition = 2
}

補足

Spinnerの場合は、アダプタのメソッドでアイコン表示に変えられますが、AutoCompleteTextViewの場合は、EditTextとリスト部分で別々に対応しないといけないです。リストに選択状態のisCheckedを反映させたい場合も少々面倒です。Spinnerは便利ですね。