我正在制作一个简单的登录表单(电子邮件和密码)来尝试增强我的反应式编程技能。我在让电子邮件字段验证按照我想要的方式工作时遇到一些问题。
这是我的代码:
final Observable<CharSequence> email = RxTextView.textChanges(emailView);
Observable<Boolean> emailIsValid = email.map(new Func1<CharSequence, Boolean>() {
@Override
public Boolean call(CharSequence charSequence) {
Log.d("asdf", "emailIsValid call: " + charSequence);
return Pattern.matches(Patterns.EMAIL_ADDRESS.pattern(), charSequence);
}
});
RxView.focusChanges(emailView)
.withLatestFrom(emailIsValid, new Func2<Boolean, Boolean, Boolean>() {
@Override
public Boolean call(Boolean hasFocus, Boolean emailIsValid) {
return (!hasFocus && !emailIsValid);
}
})
.subscribe(new Action1<Boolean>() {
@Override
public void call(Boolean showError) {
if (showError) {
emailInputLayout.setError("Enter a valid email");
} else {
emailInputLayout.setError(null);
}
}
});
Observable<CharSequence> password = RxTextView.textChanges(passwordView);
Observable.combineLatest(emailIsValid, password,
new Func2<Boolean, CharSequence, Boolean>() {
@Override
public Boolean call(Boolean emailIsValid, CharSequence password) {
Log.d("asdf", "valid: " + emailIsValid + ", password: " + password);
return (emailIsValid && password.length() > 0);
}
})
.subscribe(RxView.enabled(loginButton));
这是日志:
emailIsValid call:
emailIsValid call:
valid: false, password:
// I type 'j'
emailIsValid call: j
emailIsValid call: j
valid: false, password:
// I type 'a'
emailIsValid call: ja
emailIsValid call: ja
valid: false, password:
如你看到的,emailIsValid
每次我键入一个字符时都会调用两次,这意味着它会执行两次正则表达式匹配,这有点浪费。
我查了一下如何制作emailIsValid
每次更改只调用一次,无论有多少订阅者,我发现share()
方法。这是我添加时发生的情况.share()
到最后emailIsValid
的声明:
emailIsValid call:
// I type 'j'
emailIsValid call: j
valid: false, password:
// I type 'a'
emailIsValid call: ja
valid: false, password:
这解决了问题,但它导致了另一个问题:没有初始发射emailIsValid
to the combineLatest
函数位于最后,因此登录按钮开始启用,而实际上它应该被禁用(变灰)。
解决这个问题最干净的方法是什么?我think我希望它表现得像BehaviorSubject
,但我不确定这是否是最好的方法。