使用 RxJava2 在 ViewModel 上进行 EditText 验证
EditText validation on ViewModel using RxJava2
我尝试使用 RxJava 在单击按钮时对 EditText 执行验证,但我很难将验证移至 ViewModel,这将使测试更容易。我正在使用 Jake Wharton 的 RxBindings 获取 UI 输入,并使用 RxJava2 的 Flowable.combineLatest 和 PublishSubject 在我单击 AlertDialog 上的按钮时触发 Flowable。这是我到目前为止得到的:
private Flowable<CharSequence> projectTitleObservable;
private final PublishSubject<CharSequence> createProjectClicked = PublishSubject.create();
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final Context context = getActivity();
View dialogView = LayoutInflater.from(context).inflate(R.layout.new_project, null);
ButterKnife.bind(this, dialogView);
projectTitleObservable = RxTextView.textChanges(projectNameEditText).toFlowable(BackpressureStrategy.LATEST);
ConnectableFlowable <CharSequence> connectableFlowable = Flowable.combineLatest(createProjectClicked.toFlowable(BackpressureStrategy.LATEST), projectTitleObservable, (ignored, title) -> {
boolean validTitle = !TextUtils.isEmpty(title);
if (!validTitle) {
projectNameEditText.setError("Project must have a name");
}
Timber.d("Hey, lambdas work! Look -> " + title);
return title;
})
.publish();
connectableFlowable.subscribe(test -> Timber.d(test.toString()));
return new AlertDialog.Builder(context)
.setTitle("Add new Project")
.setView(dialogView)
.setPositiveButton(android.R.string.ok, (dialogInterface, i) -> {
Timber.d("Ok clicked!");
createProjectClicked.onNext("It works!");
})
.setNegativeButton(android.R.string.cancel, (dialogInterface, i) -> Timber.d("Cancel clicked!"))
.create();
}
我只是在同一个地方发布和订阅,以确保它在将 ConnectableFlowable 移动到 ViewModel 之前确实有效。我得到的唯一日志是 "Ok clicked!",这对我来说意味着它从未被订阅过。知道为什么它不订阅吗?
我可以使用 flatMap 实现此行为。以下是文本有效时如何显示 Toast 的示例:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val btnSubject = PublishSubject.create<Boolean>()
val textObservable = RxTextView.textChangeEvents(editText)
.filter { validateText(it.text().toString()) }
btnSubject.flatMap { textObservable }
.subscribe({
Toast.makeText(this, "Text is correct", Toast.LENGTH_SHORT).show()
})
btn.setOnClickListener {
btnSubject.onNext(true)
}
}
private fun validateText(text: String) = text.length >= 5
如果您需要在文本不正确时显示一些反馈,您可以更改地图的过滤器并像这样使用它:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val btnSubject = PublishSubject.create<Boolean>()
val textObservable = RxTextView.textChangeEvents(editText)
.map { validateText(it.text().toString()) }
btnSubject.flatMap { textObservable }
.subscribe({ isTextCorrect ->
if (isTextCorrect) {
Toast.makeText(this, "Text is correct", Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(this, "Text NOT is correct", Toast.LENGTH_SHORT).show()
}
})
btn.setOnClickListener {
btnSubject.onNext(true)
}
}
private fun validateText(text: String) = text.length >= 5
我刚刚测试了这段代码,工作正常。
快乐代码!
Java版本:
如果代码有效则显示 Toast:
private Observable<TextViewTextChangeEvent> taskTitleObservable;
private final PublishSubject<Boolean> createTaskClicked = PublishSubject.create();
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
ButterKnife.bind(this, view);
taskTitleObservable = RxTextView.textChangeEvents(taskTitle)
.filter(event -> validateText(event.text().toString()));
createTaskClicked.flatMap(test -> taskTitleObservable).subscribe( aVoid -> {
Toast.makeText(getActivity(), "Text is correct", Toast.LENGTH_SHORT).show();
});
RxView.clicks(fab).subscribe(aVoid -> {
createTaskClicked.onNext(true);
});
}
private Boolean validateText (String text) {
return text.length() >= 5;
}
文字有误请反馈:
private Observable<Boolean> taskTitleObservable;
private final PublishSubject<Boolean> createTaskClicked = PublishSubject.create();
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
ButterKnife.bind(this, view);
taskTitleObservable = RxTextView.textChangeEvents(taskTitle)
.map(event -> validateText(event.text().toString()));
createTaskClicked.flatMap(test -> taskTitleObservable).subscribe( isTextCorrect -> {
if (isTextCorrect) {
Toast.makeText(getActivity(), "Text is correct", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(getActivity(), "Text is incorrect", Toast.LENGTH_SHORT).show();
}
});
RxView.clicks(fab).subscribe(aVoid -> {
createTaskClicked.onNext(true);
});
}
private Boolean validateText (String text) {
return text.length() >= 5;
}
我尝试使用 RxJava 在单击按钮时对 EditText 执行验证,但我很难将验证移至 ViewModel,这将使测试更容易。我正在使用 Jake Wharton 的 RxBindings 获取 UI 输入,并使用 RxJava2 的 Flowable.combineLatest 和 PublishSubject 在我单击 AlertDialog 上的按钮时触发 Flowable。这是我到目前为止得到的:
private Flowable<CharSequence> projectTitleObservable;
private final PublishSubject<CharSequence> createProjectClicked = PublishSubject.create();
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final Context context = getActivity();
View dialogView = LayoutInflater.from(context).inflate(R.layout.new_project, null);
ButterKnife.bind(this, dialogView);
projectTitleObservable = RxTextView.textChanges(projectNameEditText).toFlowable(BackpressureStrategy.LATEST);
ConnectableFlowable <CharSequence> connectableFlowable = Flowable.combineLatest(createProjectClicked.toFlowable(BackpressureStrategy.LATEST), projectTitleObservable, (ignored, title) -> {
boolean validTitle = !TextUtils.isEmpty(title);
if (!validTitle) {
projectNameEditText.setError("Project must have a name");
}
Timber.d("Hey, lambdas work! Look -> " + title);
return title;
})
.publish();
connectableFlowable.subscribe(test -> Timber.d(test.toString()));
return new AlertDialog.Builder(context)
.setTitle("Add new Project")
.setView(dialogView)
.setPositiveButton(android.R.string.ok, (dialogInterface, i) -> {
Timber.d("Ok clicked!");
createProjectClicked.onNext("It works!");
})
.setNegativeButton(android.R.string.cancel, (dialogInterface, i) -> Timber.d("Cancel clicked!"))
.create();
}
我只是在同一个地方发布和订阅,以确保它在将 ConnectableFlowable 移动到 ViewModel 之前确实有效。我得到的唯一日志是 "Ok clicked!",这对我来说意味着它从未被订阅过。知道为什么它不订阅吗?
我可以使用 flatMap 实现此行为。以下是文本有效时如何显示 Toast 的示例:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val btnSubject = PublishSubject.create<Boolean>()
val textObservable = RxTextView.textChangeEvents(editText)
.filter { validateText(it.text().toString()) }
btnSubject.flatMap { textObservable }
.subscribe({
Toast.makeText(this, "Text is correct", Toast.LENGTH_SHORT).show()
})
btn.setOnClickListener {
btnSubject.onNext(true)
}
}
private fun validateText(text: String) = text.length >= 5
如果您需要在文本不正确时显示一些反馈,您可以更改地图的过滤器并像这样使用它:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val btnSubject = PublishSubject.create<Boolean>()
val textObservable = RxTextView.textChangeEvents(editText)
.map { validateText(it.text().toString()) }
btnSubject.flatMap { textObservable }
.subscribe({ isTextCorrect ->
if (isTextCorrect) {
Toast.makeText(this, "Text is correct", Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(this, "Text NOT is correct", Toast.LENGTH_SHORT).show()
}
})
btn.setOnClickListener {
btnSubject.onNext(true)
}
}
private fun validateText(text: String) = text.length >= 5
我刚刚测试了这段代码,工作正常。
快乐代码!
Java版本:
如果代码有效则显示 Toast:
private Observable<TextViewTextChangeEvent> taskTitleObservable;
private final PublishSubject<Boolean> createTaskClicked = PublishSubject.create();
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
ButterKnife.bind(this, view);
taskTitleObservable = RxTextView.textChangeEvents(taskTitle)
.filter(event -> validateText(event.text().toString()));
createTaskClicked.flatMap(test -> taskTitleObservable).subscribe( aVoid -> {
Toast.makeText(getActivity(), "Text is correct", Toast.LENGTH_SHORT).show();
});
RxView.clicks(fab).subscribe(aVoid -> {
createTaskClicked.onNext(true);
});
}
private Boolean validateText (String text) {
return text.length() >= 5;
}
文字有误请反馈:
private Observable<Boolean> taskTitleObservable;
private final PublishSubject<Boolean> createTaskClicked = PublishSubject.create();
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
ButterKnife.bind(this, view);
taskTitleObservable = RxTextView.textChangeEvents(taskTitle)
.map(event -> validateText(event.text().toString()));
createTaskClicked.flatMap(test -> taskTitleObservable).subscribe( isTextCorrect -> {
if (isTextCorrect) {
Toast.makeText(getActivity(), "Text is correct", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(getActivity(), "Text is incorrect", Toast.LENGTH_SHORT).show();
}
});
RxView.clicks(fab).subscribe(aVoid -> {
createTaskClicked.onNext(true);
});
}
private Boolean validateText (String text) {
return text.length() >= 5;
}