Android — Form input and validation using MVVM with DataBinding

Ali Muzaffar
Sep 3, 2018 · 8 min read
A simple login form using MVVM with 2-way data-binding (all using Jetpack)

There is a lot of information on the internet on how to do validations and form in Android using MVVM. I’ve however found a lot of these example don’t quite cover the feature set I would like, so I decided to create the example(s) I was seeking.

TL;DR

The issue with incomplete examples is that they result in a lot of developers defaulting to what is familiar and this results in them making decisions that violate the spirit of MVVM. When using the MVVM pattern, if I find myself holding a reference to Activity or Fragment in my ViewModel, I re-evaluate my approach. A reference to Activity or Fragment to get Context and callbacks for actions are two examples where developers often struggle. Personally the Model and ViewModel should be pure objects and have no dependencies on Fragment/Activity or Context of any nature. Firstly, doing this violates MVVM where the ViewModel should have no reference to View, instead the View about be updated through most of an event/observer pattern. Secondly, we want our code to be as testable as possible using unit testing and pure objects are just easier to test and if you hold a reference to Context in any way, this will mean having to mock the Context object for any functionality you want to use.

Disclaimer: I’m not saying that there is *no* scenario where you’ll be able to solve all your problems without a reference to a context or a callback, I’m just saying that you need to be very sure that this is the only way.

So what are we building? — Requirements:

We are going to build a simple login screen with the following features:

  • The page will have a and field as well as a button.
  • Fields should validate if they lose focus only *after* they have been modified. This means that if a field is empty, I click on it and click away, I do not want to validate the field.
  • If the fields input is too short or the format is incorrect, I want to show a field level error.
  • If both the and fields have valid input in them, I want to enable the button. The button is disabled by default.

Show me the code

With MVVM there are 2 main approaches you can take.

  • Method1 — A model Object is used to represent the form and it’s fields. The state of the form is maintained by the ViewModel and it contains methods for validating input.
  • Method2 — The input fields, the error messages and, the form each have their own model. The Form model contains all the logic for form validation and the ViewModel only exposes functionality to the View.

Which method is better? Honestly, when I started down this process, I was leaning towards . After all, code for is more concise. However, as I worked on I grew fonder of it. The main reason for this is that I found that it was easier to write unit tests for and provide greater code coverage for the code using . The operative word here is that I felt it was which can be very subjective. Since each of the fields on the form, error messages and the form itself had it’s own model. I could just test the model to test the forms behaviour without having to test the ViewModel. The ViewModel was purely exposing functionality to the View which I felt was better. Now, that is not to say that you don’t need to test the ViewModel, just that I can test the ViewModel and it’s behaviour separate from that of the Form and it’s functionality. All that being said, has a lot more code than and so will take a bit longer to do. That being said, using Kotlin could reduce the extra code and reduce the time difference to a point where it may not matter (thanks to Kotlin Data Classes).

The repo is linked below. Take a look at branches and :

Some of the code explained

I feel like there are parts of the code where people may have some questions. I’ve done my best to address some of the concerns here.

Why do some of the model objects extend while others do not?

We are using 2-way data-binding on this project. This is effectively donated with the annotation in the layout. What this means is that when the fields value is changed, the underlying model object is also changed. When we don’t use 2-way data-binding, the View only displays and does not update the model. In our layout, this is pretty much every macro that does not start with . As an optimisation, when a field is not changed, the UI does not render it again. The 2-way data-binding means that when the Email is changed, the UI for the email field is re-rendered because the value is updated, however, since our model goes ahead and updates the error states as well, the model and the View do not know that other fields have also changed, hence the changes are not visible. On order to fix this issue, we tell our api what has changed with something like where BR is a generated file that contains all the bindings. This method is only available if an object extends . only contains variables defined in the layout XML or annotated with .

How do I deal with Strings in my model when context is not available?

There may be a temptation to deal with this by creating an Application singleton. However, I would resist this urge. The best way to deal with this is to just use the resource ID’s. The data-binding will accept the resource id’s and just work, in places where you have to explicitly handle it yourself, this isn’t all that hard, just take a look at when we do for :

@BindingAdapter("error")
public static void setError(EditText editText, Object strOrResId) {
if (strOrResId instanceof Integer) {
editText.setError(
editText.getContext()
.getString((Integer) strOrResId));
} else {
editText.setError((String) strOrResId);
}
}

That code works whether you call with a String or a String resource ID.

The focus change listener code explained.

There is nothing complex here, but some people might understandable not understand why I chose to do this the way I have. The issue is that in order to bind the focus change listener, I need to get a reference to the View. In a ViewModel you should not have a reference to the View. For Android, this means that having access to Context in any form, whether as an Activity or Fragment is not a good idea. Either a reference to either, how do we get the View? There are some ways around it, but they at the very least require you to know the ID of your View. This is problematic, because it means your ViewModel can’t be used with any other View. In the current setup the ViewModel only exposes the functionality of the and it’s up to the View to bind to it. Again, this is the purpose of the ViewModel, to expose functionality to the View. Basically, we don’t care what the View is, as long as it uses the ViewModel, the app keeps working.

Why does the View go through the ViewModel to get to the Model?

It’s true that you could simply declare an other variable in the View and use a model object directly, pass that model object to the ViewModel to access functionality as needed. This does generally violate the idea that a model is your logic and a ViewModel is your layer for exposing functionality. This further means that the ViewModel can be passed in objects that it’s not meant to handle or that ViewModel is not aware of the functionality it is providing to the View. This is a problem from a encapsulation and separation of concerns point of View. As long as the model being used by the View is being exposed by the ViewModel, the ViewModel and Model can be changed with predictable results. When the user can specify their own model objects in the View and pass them to the ViewModel to achieve some action, there is no guarantee that the outcome is predictable.

Why is an instance of and not ?

It’s true that it is possible to make this code work with being an or an or even an . There are a few reasons I chose the LiveData approach. Firstly, I was going to bind to the field in the Activity so it is nice to have it be Lifecycle Aware. So, I don’t have to worry about passing data back to my activity if an activities had been opened to “Pay with PayPal” for example. When I set data in LiveData, it will be delivered to the observer (my activity) when it resumes.

Secondly, look at the code for Observable vs. MutableLiveData:

viewModel.getLoginFields().observe(this, new Observer<LoginFields>() {
@Override
public void onChanged(LoginFields loginModel) {

}
});
viewModel.getLoginFields().addOnPropertyChangedCallback(new Observable.OnPropertyChangedCallback() {
@Override
public void onPropertyChanged(Observable sender, int propertyId) {

}
});

As you can see, there is no way to use the without explicitly fetching the data from the ViewModel. Where as in the case of LiveData, the changed data is delivered right to me. Also, the “Sender” has to be an which in this example is not a problem, however, your model may not be an instance of .

When do I use vs Observable?

As a rule of thumb, if your data is being delivered to a lifecycle aware component, consider using . When you want your View to be aware of changes to a field, use (take a look at the as an example of this. If the fields are not Observable the error messages will not disable automatically).

Update 11-Sept-2018: @BindingAdapters annotated static methods only need to be defined once

It’s come to my attention that methods such as and are being defined again and again by people in each ViewModel they create and want to use this functionality.

This is not necessary.

I put these methods in the ViewModel to keep all the code in one place. When a static method is annotated with that functionality is available to all Views through data-binding. If you define the same again and again, the duplicate methods will be ignored (if your binding or is the same). Ideally these methods should probably be moved out to their own class such as a so that this is not confusing for readers.

Finally

If you’re still struggling between or , think of it as a stylistic choice. I can easily come up with a third version that takes things I like from both methods to make a 3rd way of doing things. However, that is not to say that the 3rd method would be perfect or even better than the first two. The wonderful thing about the MVVM patten is that if you decide to use and realise it’s not for you, you can change to with relative ease. One thing is for sure in order to build great Android apps, read more of my articles.

Yay! you made it to the end! We should hang out! feel free to follow me on Medium, LinkedIn, Google+ or Twitter.

BCG Digital Ventures Engineering

We build and launch solutions with transformative power

BCG Digital Ventures Engineering

BCG Digital Ventures is a corporate investment and incubation firm. We invent, build, scale and invest in startups with the world’s most influential companies. We are hiring engineers across the world: check out https://careers.bcgdv.com/cohorts/engineering

Ali Muzaffar

Written by

A software engineer, an Android, and a ray of hope for your darkest code. Residing in Sydney.

BCG Digital Ventures Engineering

BCG Digital Ventures is a corporate investment and incubation firm. We invent, build, scale and invest in startups with the world’s most influential companies. We are hiring engineers across the world: check out https://careers.bcgdv.com/cohorts/engineering