Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

RxJava and MVP in Android app

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?

like image 445
Anton Tananaev Avatar asked Oct 27 '16 01:10

Anton Tananaev


1 Answers

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();
            } 
        }
    });
like image 54
R. Zagórski Avatar answered Sep 22 '22 09:09

R. Zagórski