Search code examples
androidandroid-databindingandroid-architecture-componentsandroid-jetpacktwo-way-binding

Convert one-way data binding to two-way using Android Architecture Components


I am refactoring my Android app for an University project to use Architecture Components and I am having a hard time implementing two-way data binding on a SwitchCompat. The app has got a simple user interface with a TextView displaying the status of location updates and the aforementioned SwitchCompat, which toggles on and off location updates.
For now I am using one-way data binding on the SwitchCompat's checked attribute, but would like to use two-way databinding.
The current implementation, using a Model-View-ViewModel architecture is the following:
MainViewModel.java:

public class MainViewModel extends ViewModel {

    private LiveData<Resource<Location>> mLocationResource;

    public MainViewModel() {
        mLocationResource = Repository.getInstance().getLocationResource();
    }

    public LiveData<Resource<Location>> getLocationResource() {
        return mLocationResource;
    }

    public void onCheckedChanged (Context context, boolean isChecked) {
        if (isChecked) {
            Repository.getInstance().requestLocationUpdates(context);
        } else {
            Repository.getInstance().removeLocationUpdates(context);
        }
    }
}

Resource<Location> (saw the idea here) is a class holding nullable data (Location) and a non null state the TextView can handle:
State.java

public enum State {
    LOADING,
    UPDATE,
    UNKNOWN,
    STOPPED
}

And now the android:onCheckedChanged implementation in fragment_main.xml:

android:onCheckedChanged="@{(buttonView, isChecked) -> viewModel.onCheckedChanged(context, isChecked)}"

And finally the custom binding adapter to convert from state to boolean checked state:

@BindingAdapter({"android:checked"})
public static void setChecked(CompoundButton view, Resource<Location> locationResource) {
    State state = locationResource.getState();
    boolean checked;
    if (state == State.STOPPED) {
        checked = false;
    } else {
        checked = true;
    }
    if (view.isChecked() != checked) {
        view.setChecked(checked);
    }
}

and the implementation of the android:checked attribute in fragment_main.xml:

android:checked="@{viewModel.getLocationResource}"

As the Android Developers guide I linked above said, how can I do all the work inside android:checked instead of having both android:checked and android:onCheckedChanged (one-way databinding to two-way data binding)?
Also, please let me know if you think the architecture/logic of my app can be improved :)


Solution

  • At the end, I gave up trying to convert from one-way data binding to two-way data binding, but I managed to simplify a little bit the android:checked attribute:
    I replaced value

    "@{viewModel.getLocationResource}"
    

    with

    "@{viewModel.locationResource.state != State.STOPPED}"
    

    and completely removed the android:checked @BindingAdapter.