用一个简短的例子来说明这一点。
假设这是我要过滤的源可观察对象
Observable.interval(1, TimeUnit.SECONDS)
我使用一个复选框来处理过滤器状态。当框未选中时,我想跳过所有值。
我使用RxAndroid获得一个可观察的复选框,像这样:
RxCompoundButton.checkedChanges(checkBox)
下面是我的代码:
Observable.combineLatest(
RxCompoundButton.checkedChanges(checkBox)
, Observable.interval(1, TimeUnit.SECONDS)
, (isChecked, intervalCounter) -> {
if (!isChecked) {
return null;
} else {
return intervalCounter;
}
}
).filter(Objects::nonNull)
我得到了想要的输出
interval 1--2--3--4--5--6--7--8--9
checkbox 0-----1--------0-----1---
combineLatest
result ------3--4--5--------8--9
我刚刚开始使用RxJava,我的"解决方案"感觉不太对,因为:
您可以看到,combine函数返回一个魔术值null
,然后过滤器将跳过这些nulls
。
这意味着即使我已经知道我想跳过这个数据,也会调用过滤器函数。
- 也许我应该用另一个操作符
- 是否有办法在过滤器函数 中使用可观察的复选框?
- 可能在combine函数中有某种方式表示我想跳过这个数据
您对所需输出的图片不正确。在您的实现中,combineLatest
将null
合并到流中:
interval 1--2--3--4--5--6--7--8--9
checkbox 0-----1--------0-----1---
combineLatest N--N--3--4--5--N--N--8--9
filter(NonNull)------3--4--5--------8--9
恕我直言,在Rx流中使用null
作为信号并不好,开发人员很容易陷入NullPointerException
。
要避免使用null
,有两个措施。第一个是将结果转换为Pair,并应用滤波器&地图。
Pair
类:
public class Pair<T, U> {
T first;
U second;
public Pair(T first, U second) {
this.first = first;
this.second = second;
}
}
那么整个实现是这样的:
Observable.combineLatest(
RxCompoundButton.checkedChanges(checkBox)
, Observable.interval(1, TimeUnit.SECONDS)
, (isChecked, intervalCounter) -> new Pair<>(isChecked,intervalCounter)
).filter(pair -> pair.first)
.map(pair -> pair.second) // optional, do this if you only need the second part
数据流:
interval 1 2 3 4
| | | |
checkbox F | T | F | T F
| | | | | | |
combineLatest F1 T1 T2 F2 F3 T3 F4
| | |
filter(first=T) T1 T2 T3
| | |
map(second) 1 2 3
或者如果你可以在你的项目中使用Java 8,使用Optional
来避免null
,这与你的解决方案非常相似,但它给其他开发人员意识到流信号是Optional
。
Observable<Optional<Integer>> o1 = Observable.combineLatest(
RxCompoundButton.checkedChanges(checkBox)
, Observable.interval(1, TimeUnit.SECONDS)
, (isChecked, intervalCounter) -> {
if (!isChecked) {
return Optional.empty();
} else {
return Optional.of(intervalCounter);
}
}
)
Observable<Integer> o2 = o1.filter(Optional::isPresent).map(Optional::get)
一些更简单的方法,与combineLatest
不同,但与您想要的结果图片相同。
// Approach 1
Observable.interval(1, TimeUnit.SECONDS).filter( i -> checkBox.isEnabled())
// Approach 2
Observable.interval(1, TimeUnit.SECONDS)
.withLatestFrom(
RxCompoundButton.checkedChanges(checkBox),
(isChecked, intervalCounter) -> {
if (!isChecked) {
return Optional.empty();
} else {
return Optional.of(intervalCounter);
}
}).filter(Optional::isPresent).map(Optional::get)