Processing Multiple EditText Changes in Background

In the previous post I showed an example of how one can observe EditText changes in the background. In this post I’ll cover how to combine several EditTexts changes in one chain and observe them in the background, too.

Assume we have three EditTexts and each one of them should be checked before enabling/disabling the Button. In order to observe all CharSequences in one place, we will use combineLatest()1 method in the rx.Observable class.

First, we need to create an Observable of an CharSequence which we will pass to the combineLatest() method. We will use a RxBinding library for that. There are two requirements that we have:

  1. Avoid rx.exceptions.MissingBackpressureException. In order to address this issue we will use debounce() function like in the previous post.
  2. Observe everything in background.

Final Observable<CharSequence>:

Observable<CharSequence> charSequenceObservable = RxTextView.textChanges(editText)
    .observeOn(Schedulers.computation())
    .debounce(new Func1<CharSequence, Observable<Long>>() {
      @Override public Observable<Long> call(CharSequence charSequence) {
        // Do not debounce if the CharSequence is empty
        // because in this way we can disable the button simultaneously.
        if (charSequence.length() == 0) return Observable.just(0L);

        // Otherwise, wait for 500 millis.
        return Observable.timer(500, TimeUnit.MILLISECONDS);
      }
    })
    .subscribeOn(AndroidSchedulers.mainThread());

Suppose we extracted the Observable creation and named the method rxTextView(), so that we can create three Observables in the same manner. We can now pass all them to the Observable.combineLatest().

Observable.combineLatest(rxTextView(email),
                         rxTextView(username),
                         rxTextView(password), ...);

By using combineLatest() method we acquire a callback where we can process all emitted items of the Observables which we passed to the method. This gives us an opportunity to return whatever object we want further. In the current example we will return Boolean in order to enable/disable the button in our Activity.

This is an example of the callback we can have from three Observable<CharSequence>:

new Func3<CharSequence, CharSequence, CharSequence, Boolean>() {
  @Override public Boolean call(CharSequence email, CharSequence username, CharSequence password) {
    boolean emailValid = isEmailValid(email.toString());
    boolean usernameValid = isUsernameValid(username.toString());
    boolean passwordValid = isPasswordValid(password.toString());

    return emailValid && usernameValid && passwordValid;
  }
})

In this callback we validate all the fields and pass a Boolean further. And when subscribing we can enable/disable the Button.

.subscribe(new Observer<Boolean>() {
  final String TAG = "Observer<Boolean>";
  
  @Override public void onCompleted() {
    Log.d(TAG, "onCompleted()");
  }
  @Override public void onError(Throwable e) {
    Log.d(TAG, "onError()", e);
  }
  @Override public void onNext(Boolean enabled) {
    Log.d(TAG, String.format("onNext(enabled) -> %1$s", enabled));
    
    button.setEnabled(enabled);
  }
});

Don’t forget to save the rx.Subscription and unsubscribe() in onStop().

The sample project can be found here.

  1. when an item is emitted by either of two Observables, combine the latest item emitted by each Observable via a specified function and emit items based on the results of this function

comments powered by Disqus