RxJava2 实战知识梳理(7) - 基于 combineLatest 实现的输入表单验证

96
泽毛
1.4 2017.08.30 00:26* 字数 1022

RxJava2 实战系列文章

RxJava2 实战知识梳理(1) - 后台执行耗时操作,实时通知 UI 更新
RxJava2 实战知识梳理(2) - 计算一段时间内数据的平均值
RxJava2 实战知识梳理(3) - 优化搜索联想功能
RxJava2 实战知识梳理(4) - 结合 Retrofit 请求新闻资讯
RxJava2 实战知识梳理(5) - 简单及进阶的轮询操作
RxJava2 实战知识梳理(6) - 基于错误类型的重试请求
RxJava2 实战知识梳理(7) - 基于 combineLatest 实现的输入表单验证
RxJava2 实战知识梳理(8) - 使用 publish + merge 优化先加载缓存,再读取网络数据的请求过程
RxJava2 实战知识梳理(9) - 使用 timer/interval/delay 实现任务调度
RxJava2 实战知识梳理(10) - 屏幕旋转导致 Activity 重建时恢复任务
RxJava2 实战知识梳理(11) - 检测网络状态并自动重试请求
RxJava2 实战知识梳理(12) - 实战讲解 publish & replay & share & refCount & autoConnect
RxJava2 实战知识梳理(13) - 如何使得错误发生时不自动停止订阅关系
RxJava2 实战知识梳理(14) - 在 token 过期时,刷新过期 token 并重新发起请求
RxJava2 实战知识梳理(15) - 实现一个简单的 MVP + RxJava + Retrofit 应用


一、前言

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最后一次发射的数据,我们通过该数据进行验证。

最终运行的效果为:


二、示例解析

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 知识梳理系列:

RxJava
Web note ad 1