😇

TextFieldで入力したテキストによって入力候補をDropdownMenuに表示したい

2022/01/02に公開

やりたいこと

Jetpack Composeで、TextFieldに入力した値に応じて、候補をドロップダウンメニューで表示したい。つまり、TextFieldの入力候補を表示したい。
今回は、名前を入れたらメールアドレスを引っ張ってくる例を見ながら、実装していく。

上手くいかない…コード

正攻法で書くのであれば、以下のコードになると思うが、動かない。

var inputText by remember {
        mutableStateOf("")
    }
    var mapRemember by remember {
        mutableStateOf(mapOf("" to ""))
    }
    var expand by remember {
        mutableStateOf(false)
    }
    val focusRequester = remember { FocusRequester() }
    Box() {
        TextField(
            value = inputText,
            modifier = Modifier.focusRequester(focusRequester = focusRequester),
            onValueChange = {
                inputText = it
                mapRemember = dummyDatasetNameEmail.filterValues { key ->
                    inputText in key
                }
                expand = true
            },
            label = { Text(text = "To") }
        )
        DropdownMenu(expanded = expand, onDismissRequest = {  }) {
	    focusRequester.requestFocus()
            Log.d("sizeMap", mapRemember.size.toString())
            mapRemember.forEach { selectionOption ->
                DropdownMenuItem(onClick = { }) {
                    Text(text = "${selectionOption.value} <${selectionOption.key}>")
		    focusRequester.requestFocus()
                }
            }
	    focusRequester.requestFocus()
        }
    }
    

注目すべきは、TextFieldとDropdownMenuを並列に入れていること、そして、DropdownMenuが展開されるとフォーカスがTextFieldから外れてしまうので、focusRequester.requestFocus()を使って、フォーカスをTextFieldに戻すことをしている(凄く悪あがきで、たくさん入れているが、動かない)。
このコードでは、フォーカスがTextFieldへ戻らない。

うまくいくコード

結局、一番早いのはColumnでTextで出力してclickableにすることで、以下のコードで、上手く動きます。

fun InputToAddressView() {
    var inputText by remember {
        mutableStateOf("")
    }
    var mapRemember by remember {
        mutableStateOf(mapOf("" to ""))
    }
    Column() {
        TextField(
            value = inputText,
            modifier = Modifier.fillMaxWidth(),
            onValueChange = {
                inputText = it
                mapRemember = dummyDatasetNameEmail.filterValues { key ->
                    inputText in key
                }
            },
            label = { Text(text = "To") }
        )
        Column() {
            mapRemember.forEach { selectionOption ->
                if (selectionOption.key != "" && selectionOption.value != "") {
                    Text(text = "${selectionOption.value} <${selectionOption.key}>",
                        modifier = Modifier
                            .fillMaxWidth()
                            .clickable {
                                //ここにクリックしたときのことを書く
                            })
                }
            }
        }
    }
}


val dummyDatasetNameEmail = mapOf(
    "akira@nyanda.yo" to "Akira Kashihara",
    "akira.kashihara@nyao.wan" to "Aira Kashihara",
    "Helo@wan.nyao" to "Helo Kashihara",
)

Execution Result

まとめ

なんか腑に落ちない実装なので、もっといい方法があれば、コメントでご教授いただけると幸いです。よろしくお願い致します。

Discussion