🕌

タップやテキスト変更イベントをFlowで受け取る

2020/09/28に公開

Noteの記事を加筆修正したものです。

Corbindの紹介

Corbind
ボタンのタップやEditTextの入力イベントをFlowで受け取れるライブラリです。

Layout.xml

<LinearLayout
    android:orientation="vertical"
    android:gravity="center"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <EditText
        android:id="@+id/text"
        android:layout_width="200dp"
        android:layout_height="wrap_content" />

    <CheckBox
        android:id="@+id/checkbox"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

    <Button
        android:id="@+id/button"
        android:enabled="false"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="@{(v) -> viewModel.onClick(v)}"
        android:text="Button" />
</LinearLayout>

Layoutは得に何もする事はなくいつも通りに記載すれば大丈夫です。

ButtonのクリックイベントをFlowで受け取る

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    binding.button.clicks().onEach {
        // Buttonタップ処理
    }.launchIn(viewLifecycleOwner.lifecycleScope)
}

button.clicks()とする事でクリックイベントがFlowとして受け取れます。

EditTextの入力をFlowで受け取る

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    binding.text.textChanges().onEach {
        Log.d("BBBBBBBBBBBBBBB", it.toString())
    }.launchIn(viewLifecycleOwner.lifecycleScope)
}

FragmentにてEditTextの入力イベントをFlowで受け取るサンプルです。
EditText#.textChanges()でテキストの入力イベントがFlowでもらえます。

D/BBBBBBBBBBBBBBB: あかさたななは
D/BBBBBBBBBBBBBBB: あかさたななは
D/BBBBBBBBBBBBBBB: あかさたななは
D/BBBBBBBBBBBBBBB: あかさたななはま
D/BBBBBBBBBBBBBBB: あかさたななはま
D/BBBBBBBBBBBBBBB: あかさたななはま

雑にキー入力するとこのようなログが出力されました。
入力直後・入力して少し時間が経過してから・テキスト確定の3回テキストが渡されるようです。

EditTextの入力値にmap処理

binding.text.textChanges().map {
    Log.d("CCCCCCCCCCCCCC", "aaaa$it")
}.launchIn(viewLifecycleOwner.lifecycleScope)

Flowなので当たり前ですが、map処理なども入れる事ができます。
テキストの入力値に対してサジェスト表示する時などdebounceを入れる事で、
キー入力から0.5秒後にサジェスト用のリクエストなども可能になります。

実用例

入力チェックとチェック有無の確認

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    combine(
        binding.text.textChanges().map { it.length > 3 },
        binding.checkbox.checkedChanges(),
        transform = { text, check -> text && check }
    ).onEach {
        binding.button.isEnabled = it
    }.launchIn(viewLifecycleOwner.lifecycleScope)
}

こちらはEditTextに4文字以上入力されている&&チェックが付いている場合にボタンが活性化するサンプルです。
Flowのcombineを使って入力チェックとチェックボックスのチェック有無を確認し、
その結果をbutton.isEnabledでセットしています。

(雑に)連打対応

button.clicks().debounce(500).onEach {
  // Buttonタップ処理
}.launchIn(viewLifecycleOwner.lifecycleScope)

ボタンのクリックイベントに対してdebounceをさせることで連打されたとしても、
一回しか処理しないようにしている例です。
ただ、処理の開始が遅延されているのでユーザにとって不便ですし、
上記の例では500ms後に再タップされたら防げないので、簡易的に連打防止したい時のみ使える技になります。

Discussion