用RxJava處理複雜表單驗證問題

用RxJava處理複雜表單驗證問題

不管是簡單的登陸頁面,仍是複雜的訂單提交頁面,表單的前端驗證(好比登陸名和密碼都符合基本要求才能點亮登陸按鈕)都是必不可少的步驟。本文展現瞭如何用RxJava來方便的處理表單提交前的驗證問題,例子採用了Android上的一個簡單的登陸頁面前端

內容提要

  • 傳統的驗證方式
  • combineLatest操做符
  • 用combineLatest處理表單驗證
  • combineLatest和zip的區別

本文中所演示的例子sample代碼位於RxAndroidDemo,參見loginActivity這個文件java

RxAndroidDemo-LoginActivity

傳統的驗證方式

這裏咱們用最簡單的例子來講明,如上圖,一個email輸入和一個password輸入,下方是一個登陸的按鈕。只有當email輸入框內容含有@字符,password輸入框內容大於4個,才點亮下方的按鈕。git

首先你用EditText仍是繼承自EditText的控件,通常來講監聽它的內容,都是用addTextChangedListener。可是如何顯然登陸按鈕的enable與否是同時要判斷email和password的,兩個都成立纔可點亮。因此咱們在email的TextWatcher中除了要判斷email是否符合條件之外,還要同時判斷password是否符合條件,這樣以來就容易形成多重判斷。
試想若是你在提交一個訂單的表單,上面是十幾個輸入框,每一個輸入的內容都同時符合條件才能夠點亮「提交」按鈕,這是多麼痛苦的事情————每個輸入框的改變都要同時再判斷其餘十幾個輸入框內容是否符合(實際上此時其餘十幾個輸入框沒變化)github

combineLatest操做符

combineLatest是RxJava自己提供的一個經常使用的操做符,它接受兩個或以上的Observable和一個FuncX閉包。當傳入的Observable中任意的一個發射數據時,combineLatest將每一個Observable的最近值(Lastest)聯合起來(combine)傳給FuncX閉包進行處理。要點在於閉包

  1. combineLatest是會存儲每一個Observable的最近的值的
  2. 任意一個Observable發射新值時都會觸發操做->「combine all the Observable's lastest value together and send to Function」
    combineLatest操做符

用combineLatest處理表單驗證

首先咱們寫上email和password的驗證方法,一個須要含有@字符,一個要求字符數超過4個:app

private boolean isEmailValid(String email) {
        //TODO: Replace this with your own logic
        return email.contains("@");
    }

private boolean isPasswordValid(String password) {
    //TODO: Replace this with your own logic
    return password.length() > 4;
}

隨後,咱們針對email和password分別建立Observable,發射的值即爲各自edittext的變化的內容,而call回調方法的返回值是textWatcher中afterTextChanged方法的傳入參數:ide

Observable<String> ObservableEmail = Observable.create(new Observable.OnSubscribe<String>() {

            @Override
            public void call(final Subscriber<? super String> subscriber) {
                mEmailView.addTextChangedListener(new TextWatcher() {
                    @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) {
                        subscriber.onNext(s.toString());
                    }
                });
            }
        });

Observable<String> ObservablePassword = Observable.create(new Observable.OnSubscribe<String>() {

    @Override
    public void call(final Subscriber<? super String> subscriber) {
        mPasswordView.addTextChangedListener(new TextWatcher() {
            @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) {
                subscriber.onNext(s.toString());
            }
        });
    }
});

最後,用combineLastest將ObservableEmail和ObservablePassword聯合起來進行驗證:this

Observable.combineLatest(ObservableEmail, ObservablePassword, new Func2<String, String, Boolean>() {
            @Override
            public Boolean call(String email, String password) {
                return isEmailValid(email) && isPasswordValid(password);
            }
        }).subscribe(new Subscriber<Boolean>() {
            @Override
            public void onCompleted() {

            }

            @Override
            public void onError(Throwable e) {

            }

            @Override
            public void onNext(Boolean verify) {
                if (verify) {
                    mEmailSignInButton.setEnabled(true);
                } else {
                    mEmailSignInButton.setEnabled(false);
                }
            }
        });

onNext中的verify就是通過combineLastest對二者驗證後組合的結果。code

參見LoginActivity的bindView()方法繼承

這裏,即便表單很是複雜,實際上你須要擴展的話就很容易了:

  1. 對每一個EditText封裝一個Observable
  2. 改寫這句話,加入新的邏輯:
return isEmailValid(email) && isPasswordValid(password);

以爲爲每個EditText封裝一個Observable要寫不少重複代碼?放心,Jake Wharton大神早已經想到,RxBinding中的RxTextView就能夠解決這個問題:

Observable<CharSequence> ObservableEmail = RxTextView.textChanges(mEmailView);
Observable<CharSequence> ObservablePassword = RxTextView.textChanges(mPasswordView);

Observable.combineLatest(ObservableEmail, ObservablePassword, new Func2<CharSequence, CharSequence, Boolean>() {
    @Override
    public Boolean call(CharSequence email, CharSequence password) {
        return isEmailValid(email.toString()) && isPasswordValid(password.toString());
    }
}).subscribe(new Subscriber<Boolean>() {
    @Override
    public void onCompleted() {

    }

    @Override
    public void onError(Throwable e) {

    }

    @Override
    public void onNext(Boolean verify) {
        if (verify) {
            mEmailSignInButton.setEnabled(true);
        } else {
            mEmailSignInButton.setEnabled(false);
        }
    }
});

參見LoginActivity的bindViewByRxBinding()方法

combineLatest和zip的區別

zip是和combineLatest有點像的一個操做符,接受的參數也是兩個或多個Observable和一個閉包。可是區別在於:

  1. zip是嚴格按照順序來組合每一個Observable,好比ObservableA的第一個數據和ObservableB的第一個數據組合在一塊兒發射給FuncX來處理,二者的第N個數據組合在一塊兒發射給FuncX來處理,以此類推
  2. zip並非任意一個Observable發射數據了就觸發閉包處理,而是等待每一個Observable的第N個數據都發射齊全了才觸發

zip通常用於整合多方按照順序排列的數據。
zip

相關文章
相關標籤/搜索