RxJava處理復(fù)雜表單驗證問題

無論是簡單的登錄頁面刻坊,還是復(fù)雜的訂單提交頁面枷恕,表單的前端驗證(比如登錄名和密碼都符合基本要求才能點亮登錄按鈕)都是必不可少的步驟。本文展示了如何用RxJava來方便的處理表單提交前的驗證問題谭胚,例子采用了Android上的一個簡單的登錄頁面

內(nèi)容提要

  • 傳統(tǒng)的驗證方式
  • combineLatest操作符
  • 用combineLatest處理表單驗證
  • combineLatest和zip的區(qū)別

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

博客園對應(yīng)原文

RxAndroidDemo-LoginActivity

傳統(tǒng)的驗證方式

這里我們用最簡單的例子來說明,如上圖漏益,一個email輸入和一個password輸入蛹锰,下方是一個登錄的按鈕深胳。只有當(dāng)email輸入框內(nèi)容含有@字符绰疤,password輸入框內(nèi)容大于4個,才點亮下方的按鈕舞终。

首先你用EditText還是繼承自EditText的控件轻庆,一般來說監(jiān)聽它的內(nèi)容,都是用addTextChangedListener敛劝。但是如何顯然登錄按鈕的enable與否是同時要判斷email和password的余爆,兩個都成立才可點亮。所以我們在email的TextWatcher中除了要判斷email是否符合條件以外夸盟,還要同時判斷password是否符合條件蛾方,這樣以來就容易造成多重判斷。
試想如果你在提交一個訂單的表單上陕,上面是十幾個輸入框桩砰,每個輸入的內(nèi)容都同時符合條件才可以點亮“提交”按鈕,這是多么痛苦的事情————每一個輸入框的改變都要同時再判斷其他十幾個輸入框內(nèi)容是否符合(實際上此時其他十幾個輸入框沒變化)

combineLatest操作符

combineLatest是RxJava本身提供的一個常用的操作符释簿,它接受兩個或以上的Observable和一個FuncX閉包亚隅。當(dāng)傳入的Observable中任意的一個發(fā)射數(shù)據(jù)時,combineLatest將每個Observable的最近值(Lastest)聯(lián)合起來(combine)傳給FuncX閉包進行處理庶溶。要點在于

  1. combineLatest是會存儲每個Observable的最近的值的
  2. 任意一個Observable發(fā)射新值時都會觸發(fā)操作->“combine all the Observable's lastest value together and send to Function”


    combineLatest操作符

用combineLatest處理表單驗證

首先我們寫上email和password的驗證方法煮纵,一個需要含有@字符懂鸵,一個要求字符數(shù)超過4個:

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分別創(chuàng)建Observable行疏,發(fā)射的值即為各自edittext的變化的內(nèi)容匆光,而call回調(diào)方法的返回值是textWatcher中afterTextChanged方法的傳入?yún)?shù):

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聯(lián)合起來進行驗證:

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就是經(jīng)過combineLastest對兩者驗證后組合的結(jié)果酿联。

參見LoginActivity的bindView()方法

這里殴穴,即使表單非常復(fù)雜,實際上你需要擴展的話就很容易了:

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

覺得為每一個EditText封裝一個Observable要寫很多重復(fù)代碼采幌?放心,Jake Wharton大神早已經(jīng)想到震桶,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的區(qū)別

zip是和combineLatest有點像的一個操作符休傍,接受的參數(shù)也是兩個或多個Observable和一個閉包。但是區(qū)別在于:

  1. zip是嚴(yán)格按照順序來組合每個Observable蹲姐,比如ObservableA的第一個數(shù)據(jù)和ObservableB的第一個數(shù)據(jù)組合在一起發(fā)射給FuncX來處理磨取,兩者的第N個數(shù)據(jù)組合在一起發(fā)射給FuncX來處理,以此類推
  2. zip并不是任意一個Observable發(fā)射數(shù)據(jù)了就觸發(fā)閉包處理柴墩,而是等待每個Observable的第N個數(shù)據(jù)都發(fā)射齊全了才觸發(fā)

zip一般用于整合多方按照順序排列的數(shù)據(jù)忙厌。


zip
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市江咳,隨后出現(xiàn)的幾起案子逢净,更是在濱河造成了極大的恐慌,老刑警劉巖歼指,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件爹土,死亡現(xiàn)場離奇詭異,居然都是意外死亡踩身,警方通過查閱死者的電腦和手機胀茵,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來挟阻,“玉大人琼娘,你說我怎么就攤上這事「礁耄” “怎么了脱拼?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長拒炎。 經(jīng)常有香客問我挪拟,道長,這世上最難降的妖魔是什么击你? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任玉组,我火速辦了婚禮谎柄,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘惯雳。我一直安慰自己朝巫,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布石景。 她就那樣靜靜地躺著劈猿,像睡著了一般。 火紅的嫁衣襯著肌膚如雪潮孽。 梳的紋絲不亂的頭發(fā)上揪荣,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天,我揣著相機與錄音往史,去河邊找鬼仗颈。 笑死,一個胖子當(dāng)著我的面吹牛椎例,可吹牛的內(nèi)容都是我干的挨决。 我是一名探鬼主播,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼订歪,長吁一口氣:“原來是場噩夢啊……” “哼脖祈!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起刷晋,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤盖高,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后掏秩,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體或舞,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年蒙幻,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片胆筒。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡邮破,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出仆救,到底是詐尸還是另有隱情抒和,我是刑警寧澤,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布彤蔽,位于F島的核電站摧莽,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏顿痪。R本人自食惡果不足惜镊辕,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一油够、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧征懈,春花似錦石咬、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至亏娜,卻和暖如春焕窝,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背维贺。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工袜啃, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人幸缕。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓群发,卻偏偏與公主長得像,于是被迫代替她去往敵國和親发乔。 傳聞我的和親對象是個殘疾皇子熟妓,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,979評論 2 355

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)栏尚,斷路器起愈,智...
    卡卡羅2017閱讀 134,659評論 18 139
  • 本篇文章介主要紹RxJava中操作符是以函數(shù)作為基本單位,與響應(yīng)式編程作為結(jié)合使用的译仗,對什么是操作抬虽、操作符都有哪些...
    嘎啦果安卓獸閱讀 2,862評論 0 10
  • 作者: maplejaw本篇只解析標(biāo)準(zhǔn)包中的操作符。對于擴展包纵菌,由于使用率較低阐污,如有需求,請讀者自行查閱文檔咱圆。 創(chuàng)...
    maplejaw_閱讀 45,668評論 8 93
  • 注:只包含標(biāo)準(zhǔn)包中的操作符笛辟,用于個人學(xué)習(xí)及備忘參考博客:http://blog.csdn.net/maplejaw...
    小白要超神閱讀 2,195評論 2 8
  • 最近學(xué)習(xí)了RxJava在android中的使用,關(guān)于RxJava是啥序苏,為什么要用RxJava手幢,好在哪,這里就不敘述...
    wangxinarhatgma閱讀 923評論 0 2