Android Data Binding and MVVM Architecture Design

Photo by Juan Gomez on Unsplash

You might have heard of Data Binding. It is a library as part of the Android Jetpack that binds UI components to data sources in the XML layout and minimizes the required code for the application’s logic. It has the ability of automatic UI update whenever data is changed and you can say goodbye to the very familiar findViewById🤗 … so more than code readability it helps to reduce null pointer exceptions and memory leaks, but consider that the errors will be caught in the compile-time. Although the layout expression language gives you a lot of power, it is best to avoid nesting complex logic inside views. Complex expressions will make your layout harder to read and maintain.

Now let’s get into coding 👩‍💻 …

Prerequisite

Model-view-viewmodel (MVVM) architecture design

Setup Instructions

First of all, in this sample project, the user enters a valid id which is between 1 and 5000, and then, as a result, a Photo and its title inside the activity will be shown (with a network call).

Add the following snippet inside the app’s build.gradle file:

apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'
android {
...
dataBinding {
enabled = true
}
}
dependencies {
...
//the compiler version must be the same as the gradle version
kapt "com.android.databinding:compiler:$gradle_version"
}

If you are using Kotlin, you have to add Data Binding’s compiler as a dependency like the above code or you can enable it inside gradle.properties file of the project properties section as shown below:

android.databinding.enableV2=true

The next step to enable Data Binding is to wrap your whole XML layout with a <layout> tag as a parent. Then if you build the project, it will generate all the needed classes, which enables Data Binding. From now, all your changes related to Data Binding will be effective and there is no need to rebuild the project for each change. For binding UI components to data sources add a <data> tag inside the <layout> tag as per below:

Layout variables are used to write layout expressions. Layout expressions are placed in the value of element attributes and they use the @{} syntax. Here are some examples:

android:text="@{photoModel.title}"
android:onClick="@{photoViewModel::onGetPhotoButtonClick}"
android:visibility="@{user.age > 18 ? View.INVISIBLE: View.VISIBLE}"
android:onClick="@{() -> photoViewModel.onGetPhotoButtonClick()}"

I have defined two variables inside the <data> tag. One with the name of photoModel and type of my current Model class inside the project which is a data class, called Photo as the below snippet:

And the other variable is photoViewModel with the type of my ViewModel class,PhotoViewModel.

The @={} syntax is used for Two-way Data Binding in which the layout and the object can both send data to each other and I have used it for the <EditText> due to getting the input automatically and for that, you need to create a ViewModel class for your activity or fragment.

The ViewModel helps in keeping the view state, modify the model as the result of actions on the view, and trigger events to reflect the changes into the view.

Inside the ViewModel class, I needed to get the Id from UI, I defined an Id variable so that by pressing a button the process could start. So, I have defined the onGetPhotoButtonClick() function. Whatever value is entered in that edit text, it will be assigned automatically to the photoViewModel.id value which is var id: Int = 0 inside the below class:

As our architecture pattern says the View Model class will interact with the Repository class to send the Id to the server for getting the photo and if the response is successful we will get our photo model. For this purpose, I have defined the PhotoListener class:

For inflating the activity’s layout we’re going to follow the below new structure:

As you can see the binding variable name (ActivityPhotoBinding) is a combination of the name of the XML layout (activity_photo) without underline, with capitalized letters, and the Binding Keyword at the end. The purpose of this binding variable is to set those layout variables which are declared in the <data> block and one of our variables will be photoViewModel . After creating the instance of photoViewModel set its value inside the activity (it’s going to be as our binding view model and it will bind the data with the UI) as shown in the following code snippet:

val photoViewModel = ViewModelProvider(this, ViewModelProvider.NewInstanceFactory()).get(PhotoViewModel::class.java)

photoBinding.photoViewModel = photoViewModel
...

As I have overridden the methods of PhotoListener inside the activity, when the response is successful the below function is called and then we can set the given Photo from the server to the photoModel ‘s value:

override fun onSuccess(photoResponse: LiveData<Photo>) {
photoResponse.observe(this, Observer {
progress_bar.hide()
if (it != null) {
photoBinding.photoModel = it
showToast(getString(R.string.success))
} else {
showToast(getString(R.string.error))
}
})
}

When you’re using Data Binding, setting values into your views with a normal approach is like the way we set the <Button> or <EditText>, but sometimes you want to do something special in your setting methods. For example, we have an <ImageView> and we want to define its resource, we can do it by defining it via a @BindingAdapter in our code for it:

And then inside your XML layout for that <ImageView> add the below snippet:

app:imageResource="@{photoModel.thumbnailUrl}"

A self-learner

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store