Reputation: 2523
I am trying to implement a screen in Android app using MVP architecture and using RxJava and RxBinding on the View side.
Basically I have 2 Spinners, 1 TextEdit and a button that's disabled by default. I want to enable the button when Spinners have items selected and text field is not empty. Here is the code:
Observable.combineLatest(
RxAdapterView.itemSelections(mFirstSpinner),
RxAdapterView.itemSelections(mSecondSpinner),
RxTextView.textChanges(mEditText),
new Func3<Integer, Integer, CharSequence, Boolean>() {
@Override
public Boolean call(Integer first, Integer second, CharSequence value) {
return !TextUtils.isEmpty(value);
}
}).subscribe(new Action1<Boolean>() {
@Override
public void call(Boolean enable) {
mButton.setEnabled(enable);
}
});
The question now is how to integrate that into MVP pattern. Ideally the "business logic" of enabling the button should be in the presenter. What's the best way to achieve this? I am thinking of passing the original observers into the presenter somehow (side question is how?), and the presenter would combine those observers and it would have the logic of enabling the button. In the end, it would just call View to modify button state.
Are there any better options? Are there any good examples of MVP with RxJava on View side?
Upvotes: 5
Views: 1259
Reputation: 3576
you can enumerate your sources and pass Pair value events to a Subject/Processor where you can do the logic whether or not to enable the button and post boolean events. The guy which updates the button from the presenter will subscribe to this Subject/Processor.
Like this you have the flexibility to change sources and logic without changing the Presenter-View contract.
Essentially you can have 2 absolutely decoupled components in the Presenter:
1) component which listens to the incoming view events and produces a stream of action to enable or disable the button 2) a component which listens to enable/disable actions and updates the view respectively (this you can also achieve with Google's Binding Library)
This way you can enable multiple decoupled chains of interaction and yet easy to maintain due components triviality and clarity of the flow connections.
You can also use smth like RHub library. You can find components example here
Upvotes: 0
Reputation: 20258
My proposition:
You are on the right track. However RxBinding
logic should still be in the view. I would move the logic connected with deciding whether to enable button or not into presenter.
Define a model holding value from all fields you would like to check:
private class ViewValuesModel {
public Integer adapter1Value;
public Integer adapter2Value;
public CharSequence textValue;
public ViewValuesModel(Integer adapter1Value, Integer adapter2Value, CharSequence textValue) {
this.adapter1Value = adapter1Value;
this.adapter2Value = adapter2Value;
this.textValue = textValue;
}
}
The inside a view create an Observable
:
Observable observable = Observable.combineLatest(
RxAdapterView.itemSelections(mFirstSpinner),
RxAdapterView.itemSelections(mSecondSpinner),
RxTextView.textChanges(mEditText),
new Func3<Integer, Integer, CharSequence, ViewValuesModel>() {
@Override
public ViewValuesModel call(Integer first, Integer second, CharSequence value) {
return new ViewValuesModel(first, second, value);
}
}
)
Then pass this Observable
to presenter:
mPresenter.observeChoosableFieldChanges(observable).
Inside presenter do the rest:
observable
.map(new Func1<ViewValuesModel, Boolean>() {
@Override
public Booleancall(ViewValuesModel viewStates) {
return !TextUtils.isEmpty(viewStates.textValue);
}
})
.subscribe(new Action1<Boolean>() {
@Override
public void call(Boolean enable) {
if (enable) {
view.enableButton();
}
}
});
Upvotes: 5