DEV Community

Sadisha Nimsara
Sadisha Nimsara

Posted on • Originally published at nsadisha.Medium on

7

Android MVVM data binding with ViewModel

Hello everyone, this article is about how you can implement android MVVM architecture to your android application. I thought to right this article because I was having many troubles when I first using the android MVVM pattern. So, I wanted other people not to face same problems again.

Introduction

MVVM is an architectural design pattern, which helps us to accomplish separation of concerns. Also it is used to build reusable and testable native android applications.

What is MVVM?

MVVM stands for:

  • Model  — This holds the data of the application. It cannot directly talk to the View. Generally, it’s recommended to expose the data to the ViewModel through Observables.
  • View  — Simply this is the user interface of the application. Does not contain any application logic.
  • View Model  — It acts as a link between the Model and the View. It’s responsible for transforming the data from the Model. It provides data streams to the View. It also uses hooks or callbacks to update the View. It’ll ask for the data from the Model.


MVVM

There are 3 possible ways to bind data with a view model in android. Those are:

  1. Two way data binding
  2. Binding using LiveData
  3. Binding using RxJava

In this article, we are going to learn binding using LiveData. We will be using Kotlin as the PL. Let’s begin.

Project structure


Project structure

Add libraries and dependencies

Enable data binding and view binding libraries

In order to do this, you need to open the build.gradle file and enable those as this:

android {
    buildFeatures {
        viewBinding true
        dataBinding true
    }
}
Enter fullscreen mode Exit fullscreen mode

Add dependencies

Add these dependencies in the build.gradle file under dependencies section.

implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.5.1'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1'
Enter fullscreen mode Exit fullscreen mode

Here you have successfully completed the first step. Let’s create our model.

Create data model

To store the data within the application run time, we are using a model. So let’s create a model called Counter as below.

package com.nsadisha.mvvmtest_2.model
class Counter(_count: Int){
var count: Int = _count
fun increment(){
this.count += 1
}
override fun toString(): String {
return count.toString()
}
}
view raw Counter.kt hosted with ❤ by GitHub

Create View Model

Create a view model as below.

package com.nsadisha.mvvmtest_2.viewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.nsadisha.mvvmtest_2.model.Counter
class FirstViewModel: ViewModel() {
private var _data = MutableLiveData<Counter>().apply {
value = Counter(0)
}
val data: LiveData<Counter> = _data
fun increase(){
_data.value = _data.value.apply {
this?.increment()
}
}
}

important

Remember that we cannot directly use _data.value.increment() method, because it does not update the value in MutableLiveData object. So we assign _data.value with _data.value.apply {this?.increament()} to awoid live data updating issues.

Creating the view

Let’s create a simple view with a TextView and a Button.

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="viewModel"
type="com.nsadisha.mvvmtest_2.viewModel.FirstViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".view.FirstFragment">
<TextView
android:id="@+id/textview_first"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{viewModel.data.toString()}"
android:textSize="24sp"
app:layout_constraintBottom_toTopOf="@id/button_first"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.421" />
<Button
android:id="@+id/button_first"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="@{() -> viewModel.increase()}"
android:text="@string/next"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.301" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

Important

  • You need to remember that you need to have tag inside the tag. Otherwise it does not work.
  • To bind a value, we use “@{viewModel.data}” format.
  • We use android:onClick=”@{() -> viewModel.increase()}” format to bind events.
  • Remember to use the same name in the tag.
  • Use android:text=”@{viewModel.data.toString()}” format. not android:text=”@={viewModel.data.toString()}”.

Associated kotlin file:

package com.nsadisha.mvvmtest_2.view
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.ViewModelProvider
import com.nsadisha.mvvmtest_2.R
import com.nsadisha.mvvmtest_2.databinding.FragmentFirstBinding
import com.nsadisha.mvvmtest_2.viewModel.FirstViewModel
class FirstFragment : Fragment() {
private lateinit var viewModel: FirstViewModel
private lateinit var binding: FragmentFirstBinding
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
//getting view model from the view model provider
viewModel = ViewModelProvider(this).get(FirstViewModel::class.java)
//init binding
binding = DataBindingUtil.inflate(inflater, R.layout.fragment_first, container, false)
//set view model
binding.viewModel = viewModel
//set lifecycle owner
binding.lifecycleOwner = this
return binding.root
}
}

Important

  • Here, we are getting the view model from the ViewModelProvider, because there will be having a single instance of the view model until we destroy the view.
  • In line number 26, we initialise the binding variable from the DataBindingUtil class.
  • We have to set the binding.viewModel to our view model (Line: 29). So that we can use the view model in xml file.
  • One important thing most of the people forget to do is setting the lifecycleOwner (Line: 32). Without this, your data will update in the view model. But, your UI will not be updated.

Thank you for reading this article. If you find something valuable in this article please give it a clap and leave your thoughts. Don’t forget to share.

To be continued!

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

Top comments (0)

Billboard image

Create up to 10 Postgres Databases on Neon's free plan.

If you're starting a new project, Neon has got your databases covered. No credit cards. No trials. No getting in your way.

Try Neon for Free →

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay