ページ

2018年3月17日土曜日

RxKotlinとRxBindingでフォームの入力チェック


RxBinding

AndroidのUIコンポーネントをRx~でよろしく触れるようにしたもの

:computer:環境構築


今回はKotlinを使うのでRxBindingもKotlin用をインストール
app/build.gradle
    // RxKotlin
    compile "io.reactivex.rxjava2:rxkotlin:$rxkotlin_version"

    // RxBinding
    compile "com.jakewharton.rxbinding2:rxbinding-kotlin:$rxbinding_version"

:pencil: 実装


簡単なサンプル

よくある以下のようなフォーム画面を作成してみる。
  • 入力フィールドに正しい値が設定されている時のみSubmitボタンが押せるようにする
レイアウトはこんな感じ
xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <android.support.v7.widget.Toolbar
        android:id="@+id/profileToolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:minHeight="?attr/actionBarSize"
        android:background="@color/colorPrimary"
        app:theme="@style/ToolbarTheme"/>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:padding="5dp"
        android:orientation="vertical">

        <android.support.design.widget.TextInputLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginBottom="10dp"
            app:errorEnabled="true"
            app:hintEnabled="true"
            app:hintAnimationEnabled="true">

            <EditText
                android:id="@+id/profileName"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:maxLines="1"
                android:maxLength="20"
                android:inputType="text"
                android:hint="@string/hint_name" />

        </android.support.design.widget.TextInputLayout>

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@string/hint_area"/>

        <Spinner
            android:id="@+id/spinnerArea"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginBottom="10dp"
            android:entries="@array/areas" />

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="30dp"
            android:paddingStart="50dp"
            android:paddingEnd="50dp"
            android:gravity="center">

            <Button
                android:id="@+id/profileSubmit"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:background="@drawable/round_shape_disabled"
                android:enabled="false"
                android:text="@string/save"
                android:textColor="@android:color/white"/>

        </LinearLayout>

    </LinearLayout>
</LinearLayout>
image.png (43.9 kB)
名前とエリアを選択するフォーム画面
名前やエリアが未選択の場合Submitボタンが押せない状態にする
kotlin
        val nameChanges = profileName.textChanges()
        val areaChanges = spinnerArea.itemSelections()

        disposable += Observables.combineLatest(nameChanges, areaChanges)
                    {name, area -> name.isNotEmpty() && area != AdapterView.INVALID_POSITION}
                    .subscribe { isValid ->
                        profileSubmit.isEnabled = isValid
                        profileSubmit.setBackgroundResource(
                                if (isValid)
                                    R.drawable.round_shape_enabled
                                else
                                    R.drawable.round_shape_disabled)
                    }
名前とエリアのObservableをcombineLatestで結合し、
両方の値チェックでOKだった場合のみボタンを有効化しています。
name.isNotEmpty() && area != AdapterView.INVALID_POSITION
-> 名前が空でなく、spinnerの選択位置が空でない

オペレータ

  • distinctUntilChanged
    連続して重複したデータを通知しない
    kotlin
        disposable += Observable.fromArray(1, 2, 2, 3, 4, 5, 5, 6)
                .distinctUntilChanged()
                .subscribe(::println) // 1,2,3,4,5,6
    
  • combineLatest
    どちらか1つの送信される時、指定された関数によって、各Observableから送信される最新のアイテムを結合し、この関数の評価に基づいたアイテムを送信する
    kotlin
            val o1 = PublishSubject.create<Int>()
            val o2 = PublishSubject.create<String>()
    
            disposable += Observables.combineLatest(
                                    o1, o2
                                    ) { n, s -> "${n}/${s}"}
                                    .subscribe(::println)
            o1.onNext(1)
            o2.onNext("a")
            o1.onNext(2)
            o1.onNext(3)
            o2.onNext("b")
            // 1/a
            // 2/a
            // 3/a
            // 3/b
    

:bomb: バッドノウハウ


自分だけかもしれませんが、Observables.combineLatestObservable.combineLatest
(Observable(s)の違い)と書くとエラーになります。。

0 件のコメント:

コメントを投稿