[Android] RxBindingでViewのアクションをSubscribeする

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

背景

調べ物をしているとViewObservableとかWidgetObservableを見つけ、やってみようと思ったらうまく出来ませんでした。 調べてみるとRxbindingに含まれるようになり、書き方も少し変更があったようなので試してみようと思います。

実装

Screenshot_20160318-005641

まずはEditTextを2つとボタンを1つ用意します。
EditTextの状態を常に見て両方記入されるとボタンがアクティブになるという物を目指して書いてみます。

    compile 'io.reactivex:rxjava:1.0.14'
    compile 'io.reactivex:rxandroid:1.0.1'
    compile 'com.jakewharton.rxbinding:rxbinding:0.2.0'

まずbuild.gradleには上のように記述します。

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.widget.Button;
import android.widget.EditText;

import com.jakewharton.rxbinding.widget.RxTextView;
import com.jakewharton.rxbinding.widget.TextViewTextChangeEvent;

import rx.Observable;
import rx.functions.Action1;
import rx.functions.Func1;
import rx.functions.Func2;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        final Button button = (Button) findViewById(R.id.submit_button);
        button.setEnabled(false);
        EditText emailText = (EditText) findViewById(R.id.email_text);
        EditText passwordText = (EditText) findViewById(R.id.password_text);

        Observable<Boolean> emailIsWritten = RxTextView.textChangeEvents(emailText)
                .map(new Func1<TextViewTextChangeEvent, Boolean>() {
                    @Override
                    public Boolean call(TextViewTextChangeEvent textViewTextChangeEvent) {
                        return !TextUtils.isEmpty(textViewTextChangeEvent.text());
                    }
                });
        Observable<Boolean> passwordIsWritten = RxTextView.textChangeEvents(passwordText)
                .map(new Func1<TextViewTextChangeEvent, Boolean>() {
                    @Override
                    public Boolean call(TextViewTextChangeEvent textViewTextChangeEvent) {
                        return !TextUtils.isEmpty(textViewTextChangeEvent.text());

                    }
                });

        Observable.combineLatest(emailIsWritten, passwordIsWritten, new Func2<Boolean, Boolean, Boolean>() {
            @Override
            public Boolean call(Boolean emailisenabled, Boolean passwordisenabled) {
                return emailisenabled && passwordisenabled;
            }
        }).subscribe(new Action1<Boolean>() {
            @Override
            public void call(Boolean isValid) {
                if (isValid) {
                    button.setEnabled(true);
                } else {
                    button.setEnabled(false);
                }
            }
        });
    }
}

これまではViewObservable.xxxxのように書いていた箇所がrxbindingに変更後はRxTextView.xxxxRxRadioGroup.xxxxになったようです。

ポイントは42行目のcombineLatestですね。
第1、第2引数にObservableを渡し、第3引数にリスナーを渡しています。Observable.combineLatestに26行目から40行目で定義した2つのObservableを渡すと、2つのObservableの内どちらに変更があっても44行目のpublic Boolean call(Boolean, Boolean)が呼ばれます。その結果をsubscribeして両方1文字以上あればボタンをアクティブにしています。

Screenshot_20160318-005728     Screenshot_20160318-005718

少しわかりにくいかもしれませんが右の画像のボタンがアクティブになっています。

RxTextView以外にRxRadioGroupやRxSearchViewがありました。 あとRxViewというものがあるのでViewであればアクションを取得できるようですね。

最後に

今回はObservable.combineLatestで2つのViewの状態の変化を見ましたが、もっと増やせば色々とできることがありそうですね。