RxJava2 實戰知識梳理(7) 基於 combineLatest 實現的輸入表單驗證

澤毛發表於2017-12-21

一、前言

2.1 應用背景

在平時的應用中,我們經常會讓使用者輸入一些資訊,最常見的莫過於註冊或者登入介面中,讓使用者輸入使用者名稱或者密碼,但是我們經常會對使用者名稱或者密碼有一定的要求,只有當它們同時滿足要求時,才允許使用者進行下一步的操作。

這個需求就涉及到一種模型,即在多個地方監聽變化,但是在一個地方進行統一驗證,如果驗證成功,那麼允許使用者進行下一步的操作,否則提示使用者輸入不正確。

通過這個例子,大家將學習到combineLatest操作符的用法。

2.2 示例

在下面這個示例中,包含了兩個輸入框,分別對應使用者名稱和密碼,它們的長度要求分別為2~84~16,如果兩者都正確,那麼登入按鈕的文案變為“登入”,否則顯示“使用者名稱或密碼無效”。

public class CombineLatestActivity extends AppCompatActivity {

    private EditText mEtName;
    private EditText mEtPassword;
    private Button mBtLogin;
    private PublishSubject<String> mNameSubject;
    private PublishSubject<String> mPasswordSubject;
    private CompositeDisposable mCompositeDisposable;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_combine_latest);
        mEtName = (EditText) findViewById(R.id.et_name);
        mEtPassword = (EditText) findViewById(R.id.et_password);
        mBtLogin = (Button) findViewById(R.id.bt_login);
        mNameSubject = PublishSubject.create();
        mPasswordSubject = PublishSubject.create();
        mEtName.addTextChangedListener(new EditTextMonitor(mNameSubject));
        mEtPassword.addTextChangedListener(new EditTextMonitor(mPasswordSubject));
        Observable<Boolean> observable = Observable.combineLatest(mNameSubject, mPasswordSubject, new BiFunction<String, String, Boolean>() {

            @Override
            public Boolean apply(String name, String password) throws Exception {
                int nameLen = name.length();
                int passwordLen = password.length();
                return nameLen >= 2 && nameLen <= 8 && passwordLen >= 4 && passwordLen <= 16;
            }

        });
        DisposableObserver<Boolean> disposable = new DisposableObserver<Boolean>() {

            @Override
            public void onNext(Boolean value) {
                mBtLogin.setText(value ? "登入" : "使用者名稱或密碼無效");
            }

            @Override
            public void onError(Throwable e) {

            }

            @Override
            public void onComplete() {

            }

        };
        observable.subscribe(disposable);
        mCompositeDisposable = new CompositeDisposable();
        mCompositeDisposable.add(disposable);
    }

    private class EditTextMonitor implements TextWatcher {

        private PublishSubject<String> mPublishSubject;

        EditTextMonitor(PublishSubject<String> publishSubject) {
            mPublishSubject = publishSubject;
        }

        @Override
        public void beforeTextChanged(CharSequence s, int start, int count, int after) {

        }

        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {

        }

        @Override
        public void afterTextChanged(Editable s) {
            mPublishSubject.onNext(s.toString());
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mCompositeDisposable.clear();
    }
}
複製程式碼

在上面的例子中,我們首先建立了兩個PublishSubject,分別用於使用者名稱和密碼的訂閱,然後通過combineLatest對這兩個PublishSubject進行組合。這樣,當任意一個PublishSubject傳送事件之後,就會回撥combineLatest最後一個函式的apply方法,該方法會取到每個被觀察的PublishSubject最後一次發射的資料,我們通過該資料進行驗證。

最終執行的效果為:

RxJava2 實戰知識梳理(7)   基於 combineLatest 實現的輸入表單驗證

二、示例解析

2.1 combineLatest 原理

combineLatest的原理圖如下所示:

combineLatest 原理圖
該操作符接受多個Observable以及一個函式作為引數,並且函式的簽名為這些Observable發射的資料型別。當以上的任意一個Observable發射資料之後,會去取其它Observable 最近一次發射的資料,回撥到函式當中,但是該函式回撥的前提是所有的Observable至少發射過一個資料項

2.2 combineLatest 和 zip 的區別

zipcombineLatest接收的引數格式相同,我們在 RxJava2 實戰知識梳理(4) - 結合 Retrofit 請求新聞資訊 中用它來實現等待多個Observable都發射之後才進行資料的組合,回顧一下它的原理圖:

zip 原理圖
zipcombineLatest的區別在於:

  • zip是在其中一個Observable發射資料項後,組合所有Observable最早一個未被組合的資料項,也就是說,組合後的Observable發射的第n個資料項,必然是每個源由Observable各自發射的第n個資料項構成的。
  • combineLatest則是在其中一個Observable發射資料項後,組合所有Observable所發射的最後一個資料項(前提是所有的Observable都至少發射過一個資料項)。

更多文章,歡迎訪問我的 Android 知識梳理系列:

相關文章