Those who are free of resentful thoughts surely find peace. - Buddha
Posted on 27th Nov 2017
Now let’s look at the ModelViewViewModel architecture and how the three components in this architectural pattern work together.
In the MVVM architectural pattern, the view and the view model mainly interact with each other through data binding. Ideally, the view and view model should not know about each other. The bindings should be the glue between view and view model and handle most of the stuff in both directions. In Android however, they can not really be independent:
For these cases, both the view and the view model should implement interfaces, which are then used for communication via commands, if necessary. In almost all cases, however, only an interface for the view model is needed, since the data binding library handles the interactions with the view, and custom components can be used e.g. when a context is needed.
The view model also updates the model, e.g. by adding a new element to the database or updating an existing one. It is also used to fetch data from the model. Ideally, the model should also notify the view model of changes, but this depends on the implementation.
Now, generelly speaking, the separation of view and view model makes the presentation logic easily testable and also helps with maintenance in the long run. Together with the data binding library this means less code and cleaner code.
<layout xmlns:android="...">
<data>
<variable name="vm" type="pkg.MyViewModel" />
</data>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="@{vm.shouldShowText}"
android:text="@={vm.text}" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="@{vm::onButtonClick}"
android:text="@string/button"/>
</FrameLayout>
</layout>
public class MyViewModel extends BaseObservable {
private Model model = new Model();
public void setModel(Model model) {
this.model = model;
notifyChange();
}
public boolean shouldShowText() {
return model.isTextRequired();
}
public void setText(String text) {
model.setText(text);
}
public String getText() {
return model.getText();
}
public void onButtonClick(View v) {
// Save data
}
}
Here we have a text property. As we have an EditText for user input, we can use two way data-binding, to also have the data binding library save the inputs back to the view model. For this, we create both a setter and a getter and bind the property to the text attribute of our EditText, but this time with an = sign before the bracket, which signals the library that we want two way data binding here.
Also, we only want to show the EditText when our model says that text input is required. For this, we provide a boolean property in our view model and bind it to the visibility attribute. For this to work, we also have to create a binding adapter, which sets the visibility to GONE when false and VISIBLE when true.
@BindingAdapter("android:visibility")
public static void setVisibility(View view, boolean visible) {
view.setVisibility(visible ? View.VISIBLE : View.GONE);
}
onButtonClick()
in our view model, which handles interacting with the model. In the layout, we bind the command to the onClick attribute of the Button via a method reference. For this to work directly, our method needs to have a single parameter of type View, just like an OnClickListener
. As an alternative – if you don’t want the View parameter – you could also use lambda expressions directly in the layout. As you can see, it’s quite easy and straight forward to use data binding with a view model.Good, better, best. Never let it rest. Untill your good is better and your better is best. - St. Jerome