はじめに
Jetpack ComposeのTextFieldには、デフォルトでタップするとフォーカスを取得し、キーボードを開くためのイベントが設定されています。
キーボード入力ではなく、別のイベント(例:DatePickerDialogを開く)を設定したい場合、
- readOnly = trueを設定する
- Modifier.clickableを設定する
ことで対応できるのでは?と思いました。
しかし、実際に実装してみると
タップしても反応しないという問題に遭遇しました。
この記事では、その原因と解決策を整理していきます。
やりたいことと出てきた課題
今回はOutlinedTextFieldを使って、以下のような UI を実現したいと考えていました。
- 見た目は
OutlinedTextField-
DatePickerDialogで選択した日付を表示する
-
- フォームをタップすると
DatePickerDialogを起動する - キーボードから直接入力はできない
上記を実現するために、次のような実装でいけると思いました。
OutlinedTextField(
value = value,
onValueChange = {},
readOnly = true,
modifier = Modifier.clickable {
// DatePickerDialog を起動する処理
onOpenDialog()
}
)
しかし、実際にはタップしてもonClick内の処理が呼ばれず、DatePickerDialogが起動しませんでした。
原因を調べると、TextField自体がデフォルトでキーボードを起動するイベントを設定しており、そことイベント設定が競合して無効になっていることがわかりました。
解決策
解決策として、ジェスチャーを低レベルで検出できるModifier.pointerInputを使いました。
pointerInputを使うことで、TextFieldが内部でイベントを処理する前の段階でタップを検知できます。
OutlinedTextField(
value = value,
onValueChange = {},
readOnly = true,
modifier = Modifier.pointerInput(Unit) {
// ジェスチャーの処理を監視
awaitEachGesture {
// タップ開始(DOWN)
awaitFirstDown(pass = PointerEventPass.Initial)
// タップ終了(UP)またはキャンセルを待つ
val up = waitForUpOrCancellation(pass = PointerEventPass.Initial)
// 正常にタップされた場合のみ処理
if (up != null) {
onOpenDialog()
}
}
}
)