用RxJava处理复杂表单验证问题

无论是简单的登录页面,还是复杂的订单提交页面,表单的前端验证(比如登录名和密码都符合基本要求才能点亮登录按钮)都是必不可少的步骤。本文展示了如何用RxJava来方便的处理表单提交前的验证问题,例子采用了Android上的一个简单的登录页面

内容提要

  • 传统的验证方式
  • combineLatest操作符
  • 用combineLatest处理表单验证
  • combineLatest和zip的区别

本文中所演示的例子sample代码位于RxAndroidDemo,参见loginActivity这个文件

传统的验证方式

这里我们用最简单的例子来说明,如上图,一个email输入和一个password输入,下方是一个登录的按钮。只有当email输入框内容含有@字符,password输入框内容大于4个,才点亮下方的按钮。

首先你用EditText还是继承自EditText的控件,一般来说监听它的内容,都是用addTextChangedListener。但是如何显然登录按钮的enable与否是同时要判断email和password的,两个都成立才可点亮。所以我们在email的TextWatcher中除了要判断email是否符合条件以外,还要同时判断password是否符合条件,这样以来就容易造成多重判断。

试想如果你在提交一个订单的表单,上面是十几个输入框,每个输入的内容都同时符合条件才可以点亮“提交”按钮,这是多么痛苦的事情————每一个输入框的改变都要同时再判断其他十几个输入框内容是否符合(实际上此时其他十几个输入框没变化)

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处理表单验证

首先我们写上email和password的验证方法,一个需要含有@字符,一个要求字符数超过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分别创建Observable,发射的值即为各自edittext的变化的内容,而call回调方法的返回值是textWatcher中afterTextChanged方法的传入参数:

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联合起来进行验证:

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对两者验证后组合的结果。

参见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一般用于整合多方按照顺序排列的数据。

用RxJava处理复杂表单验证问题的更多相关文章

  1. jQuery学习之路(8)- 表单验证插件-Validation

    ▓▓▓▓▓▓ 大致介绍 jQuery Validate 插件为表单提供了强大的验证功能,让客户端表单验证变得更简单,同时提供了大量的定制选项,满足应用程序各种需求.该插件捆绑了一套有用的验证方法,包括 ...

  2. 玩转spring boot——AOP与表单验证

    AOP在大多数的情况下的应用场景是:日志和验证.至于AOP的理论知识我就不做赘述.而AOP的通知类型有好几种,今天的例子我只选一个有代表意义的“环绕通知”来演示. 一.AOP入门 修改“pom.xml ...

  3. form表单验证-Javascript

    Form表单验证: js基础考试内容,form表单验证,正则表达式,blur事件,自动获取数组,以及css布局样式,动态清除等.完整代码如下: <!DOCTYPE html PUBLIC &qu ...

  4. ASP.NET MVC5+EF6+EasyUI 后台管理系统(33)-MVC 表单验证

    系列目录 注:本节阅读需要有MVC 自定义验证的基础,否则比较吃力 一直以来表单的验证都是不可或缺的,微软的东西还是做得比较人性化的,从webform到MVC,都做到了双向验证 单单的用js实现的前端 ...

  5. 实现跨浏览器html5表单验证

    div:nth-of-type(odd){ float: left; clear: left; } .origin-effect > div:nth-of-type(even){ float: ...

  6. jQuery Validate 表单验证 — 用户注册简单应用

    相信很多coder在表单验证这块都是自己写验证规则的,今天我们用jQuery Validate这款前端验证利器来写一个简单的应用. 可以先把我写的这个小demo运行试下,先睹为快.猛戳链接--> ...

  7. jquery validate表单验证插件-推荐

    1 表单验证的准备工作 在开启长篇大论之前,首先将表单验证的效果展示给大家.     1.点击表单项,显示帮助提示 2.鼠标离开表单项时,开始校验元素  3.鼠标离开后的正确.错误提示及鼠标移入时的帮 ...

  8. 表单验证插件之jquery.validate.js

    提到表单验证的插件,第一个想到的就是jquery.validate.js,所以小生想在这里稍微详细地说一下这款插件的具体使用方法,便于理解,我直接附上整段demo的代码(没怎么调样式,主要是看js): ...

  9. 走进AngularJs 表单及表单验证

    年底了越来越懒散,AngularJs的学习落了一段时间,博客最近也没更新.惭愧~前段时间有试了一下用yeoman构建Angular项目,感觉学的差不多了想做个项目练练手,谁知遇到了一系列问题.yeom ...

随机推荐

  1. 磁带机Media is unrecognized

    早晨检查磁带备份作业时,发现有个驱动的作业一直处于"Queue"状态,检查发现驱动有磁带,在Alert里面发现出现下面"Media is unrecognized&quo ...

  2. Protocol 编码的三种常用方式

    1.使用固定长度 2.使用固定长度的请求头,请求头中说明了body的长度. 例如HTTP 协议: http请求协议: http 响应协议: 3.使用界定符.例如有很多基于text(文本)协议都会在每个 ...

  3. SQL 扩展事件

    在本篇,我通过使用新建“Session ”对话框来创建新的扩展事件会话.定义一个自己的扩展事件,动作和谓词,并且发布一个以收集事件数据为目的的会话. 首先从UI开始 在SQLServer2008R2以 ...

  4. PHP 替换标签和标签内的内容

    $filter_arr=array('/#(.*?)#/','/\$(.*?)\$/','/\^(.*?)\^/');//要替换的标签 $content=$data['Monthlys']['cont ...

  5. 按要求编写Java应用程序: (1)编写西游记人物类(XiYouJiRenWu) 其中属性有:身高(height),名字(name),武器(weapon) 方法有:显示名字(printName),显示武器(printWeapon) (2)在主类的main方法中创建二个对象:zhuBaJie,sunWuKong。并分别为他 们的两个属性(name,weapon)赋值,最后分别调用printName,

    package com.hanqi.test; public class xiyoujirenwu { private double height;// 身高 private String name; ...

  6. Java api 入门教程 之 JAVA的StringBuffer类

    StringBuffer类和String一样,也用来代表字符串,只是由于StringBuffer的内部实现方式和String不同,所以StringBuffer在进行字符串处理时,不生成新的对象,在内存 ...

  7. spring服务定位器类

    此文章是基于 搭建SpringMVC+Spring+Hibernate平台 功能:通过持有的Spring应用场景ApplicationContext,可在任何地方获取bean. 1. 服务定位器类:S ...

  8. Sybase PowerDesign 导入数据库结构formSqlserver

    采用Sybase PD 创建数据库设计是常见的方法,如果遇到链接数据源时,无法直接链接系统数据源,而且在Sybase PD中无法直接创建odbc数据源时, 可以到控制面板中创建数据源,一步步的网络上有 ...

  9. bootstrap简单的过一遍

    注:.xxxx 表示类(class=xxxx) <h1>到<h6>均可使用.另外还提供了.h1到.h6的class .lead可以让段落突出显示 <small>  ...

  10. android canvas d

    (以下转自:http://blog.csdn.net/longyi_java/article/details/6930480) 1.基本的绘制图片方法 //Bitmap:图片对象,left:偏移左边的 ...