2017-06-13
在这一篇中, 我将继续对 RxJava-Android-Samples 项目的学习, 主要包含以下 3 个 Demo:
这 3 个 Demo 都是与 UI 相关的, 包括监听控件的事件, 以及比较流行的 Data Binding. 通过学习, 我们将会学到如何将 RxJava 应用到界面开发中.
这个 Demo 位于 BufferDemoFragment. 它的界面和说明如下:
截图 | 说明 |
---|---|
功能是统计 2s 内点击按钮的次数. 每次点击会立即展示一个 "GOT A TAP", 等 2s 结束后会显示者两秒内点击的次数. |
下面来看具体的代码:
RxView.clicks(_tapBtn) .map( onClickEvent -> { _log("GOT A TAP"); return 1; }) .buffer(2, TimeUnit.SECONDS) .observeOn(AndroidSchedulers.mainThread()) .subscribeWith( new DisposableObserver<List<Integer>>() { @Override public void onComplete() {} @Override public void onError(Throwable e) {} @Override public void onNext(List<Integer> integers) { if (integers.size() > 0) { _log(String.format("%d taps", integers.size())); } else { Timber.d("--------- No taps received "); } } });
从代码中可以看到, 在按钮上面套了一层 RxView.clicks
. 这里用到了 jakewharton 大神的 RxBinding 库.
这个库我打算在下一篇博客中详细介绍, 在这里只需知道对 Button 使用 RxView.clicks(_tapBtn)
包一下就将 Button 的点击变成了一个被观察者.
除此之外, 在上面代码中主要使用了 buffer 操作符, 它起了关键地作用, 下面来详细看一下.
其中的关键就在与 Buffer 这个操作符, 首先介绍一下它.
Buffer 操作符的官方文档在这里, 它的描述为:
具体来说, 就是 Buffer 每隔一定周期收集源被观察者发出的数据, 将它打包成一个列表, 对外发出.
因此, 上述代码的大体流程, 使用 RxJava 的弹珠图来梳理为:
其中:
buffer(2, TimeUnit.SECONDS)
表示定时的周期为 2s onNext
的参数是 List<Integer> integers
它的长度表示这段时间内点击按钮的次数这个 Demo 位于 DebounceSearchEmitterFragment. 它的界面跟说明如下:
截图 | 说明 |
---|---|
我们在输入框中输入文本, 当我们输入停下来的时候, 会自动在下方添加一条 "Searching for …" 的记录. |
对应的实现代码为:
_disposable = RxTextView.textChangeEvents(_inputSearchText) .debounce(400, TimeUnit.MILLISECONDS) // default Scheduler is Computation .filter(changes -> isNotNullOrEmpty(changes.text().toString())) .observeOn(AndroidSchedulers.mainThread()) .subscribeWith(_getSearchObserver());
其中, EditText 被套了一个 RxTextView.textChangeEvents
, 这个还是 RxBinding 库 中的方法, 将 EditText 变成了一个被观察者, 每当文本变化, 会发出事件.
除此, 最重要的是用了一个 debounce 操作符, 下面来详细看一下.
Debounce 的文档在这里, 它的描述为:
这里要注意的一点是:
我们结合上面的代码来理解这段描述:
这样, 我们通过 Debounce 操作符, 就为 EditText 提供了能够动态感知输入结束的能力.
下面我们再来看 filter 的作用.
Filter 的文档在这里, 它的描述为:
这样, 结合上面的代码不难看出, 将 Debounce 输出的数据传入 Filter, 目的是加入对 EditText 的判空检查.
如果 EditText 的内容是空的, Filter 就将它过滤掉, 不再对外发出了.
在了解了 Debounce 和 Filter 两个操作符的功能后, 我们就完全弄明白了这个 Demo 中 EditText 够动态感知输入结束的原理了.
这个 Demo 位于 DoubleBindingTextViewFragment. 它的界面和截图如下:
截图 | 说明 |
---|---|
界面中有两个用于输入数字的 EditText, 当我们改变其中的一个的值, 下方的和就会自动变化. |
这个功能是怎么实现的呢?
首先我们通过 ButterKnife 监听两个 EditText 的 OnTextChanged 事件:
@OnTextChanged({R.id.double_binding_num1, R.id.double_binding_num2}) public void onNumberChanged() { ... }
这个是 ButterKnife 提供的特性, 跟 RxJava 无关, 跟 RxJava 有关的在方法的实现里, 它的实现为:
@OnTextChanged({R.id.double_binding_num1, R.id.double_binding_num2}) public void onNumberChanged() { float num1 = 0; float num2 = 0; if (!isEmpty(_number1.getText().toString())) { num1 = Float.parseFloat(_number1.getText().toString()); } if (!isEmpty(_number2.getText().toString())) { num2 = Float.parseFloat(_number2.getText().toString()); } _resultEmitterSubject.onNext(num1 + num2); }
其中:
_resultEmitterSubject
现在的问题是 _resultEmitterSubject
是什么呢? 我们来看它的创建的代码:
_resultEmitterSubject = PublishProcessor.create();
从中可以看出, 它是一个 PublishProcessor
.
可是问题又来了, PublishProcessor
是什么呢?
对这部分的讲解记录在 What's different in 2.0 的 Subjects and Processors 一节.
还记得第一篇中介绍过 ReactiveX 中的 Subject 的概念, 它在 RxJava 2.0 中细分为两种:
对于背压这个概念, 我还不太明白. 那么暂且在这里就把 PublishProcessor 当做 PublishSubject 来看待吧.
先梳理一下前面的流程, 如下图所示:
其中, 我们已经分析了 OnTextChanged
, 调用 _resultEmitterSubject
的 onNext
. 现在还剩下最后一步, 就是观察者订阅 _resultEmitterSubject
接收数据, 这部分的实现代码为:
_disposable = _resultEmitterSubject.subscribe( aFloat -> { _result.setText(String.valueOf(aFloat)); });
这个很简单, 就是收到前面过程传入的合值, 在结果 TextView 中进行显示.
至此, 我们就完成了对 3 个 Demo 的学习.
可以看出, 结合 RxJava 进行界面开发, 代码是比较优雅的, 尤其是在实现一些对控件的扩展操作, 例如 throttling 和监听输入.
在学习中我们还看到, jakewharton 大神的 RxBinding 库为开发提供了很多便利.
这个库的功能十分强大, 在下一篇的学习中, 我将对这个库进行详细地学习.