不管是簡單的登陸頁面,仍是複雜的訂單提交頁面,表單的前端驗證(好比登陸名和密碼都符合基本要求才能點亮登陸按鈕)都是必不可少的步驟。本文展現瞭如何用RxJava來方便的處理表單提交前的驗證問題,例子採用了Android上的一個簡單的登陸頁面前端
本文中所演示的例子sample代碼位於RxAndroidDemo,參見loginActivity這個文件java
這裏咱們用最簡單的例子來講明,如上圖,一個email輸入和一個password輸入,下方是一個登陸的按鈕。只有當email輸入框內容含有@字符,password輸入框內容大於4個,才點亮下方的按鈕。git
首先你用EditText仍是繼承自EditText的控件,通常來講監聽它的內容,都是用addTextChangedListener。可是如何顯然登陸按鈕的enable與否是同時要判斷email和password的,兩個都成立纔可點亮。因此咱們在email的TextWatcher中除了要判斷email是否符合條件之外,還要同時判斷password是否符合條件,這樣以來就容易形成多重判斷。
試想若是你在提交一個訂單的表單,上面是十幾個輸入框,每一個輸入的內容都同時符合條件才能夠點亮「提交」按鈕,這是多麼痛苦的事情————每個輸入框的改變都要同時再判斷其餘十幾個輸入框內容是否符合(實際上此時其餘十幾個輸入框沒變化)github
combineLatest是RxJava自己提供的一個經常使用的操做符,它接受兩個或以上的Observable和一個FuncX閉包。當傳入的Observable中任意的一個發射數據時,combineLatest將每一個Observable的最近值(Lastest)聯合起來(combine)傳給FuncX閉包進行處理。要點在於閉包
首先咱們寫上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()方法繼承
這裏,即便表單很是複雜,實際上你須要擴展的話就很容易了:
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()方法
zip是和combineLatest有點像的一個操做符,接受的參數也是兩個或多個Observable和一個閉包。可是區別在於:
zip通常用於整合多方按照順序排列的數據。