Android Kotlin Live Data & Mutable Live Data Example

Abuhasanbaskara
3 min readMay 17, 2022

--

Live data or Mutable Live Data is an observable data holder class. We need this class because it makes it easier to handle the data inside View Model because it’s aware of Android Lifecycles such as app components, activities, fragments, and services. The data will always update and aware of its Lifecycles. Live Data vs Mutable Live Data: Live Data is only readable but Mutable Live Data is editable but both of them are necessary to use for encapsulation. I will show it later how to do it.

You can read the complete guide here :
https://developer.android.com/topic/libraries/architecture/livedata

You can download the full code of the app example here:
https://github.com/abuhasanbaskara/SimpleLiveDataAndMutable.git

First as usual we need to implement libraries in the module Gradle for Data Binding.

buildFeatures{
dataBinding true
}

Here is to implement View Model and Live Data

def lifecycle_version = "2.2.0"
// ViewModel
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
// LiveData
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"

Then we make View Model Factory for initiating the first value.

import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider

class MainActivityViewModelFactory(private val startingTitle: String, private val startingResult: Int)
: ViewModelProvider.Factory{
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(MainActivityViewModel::class.java)){
return MainActivityViewModel(startingTitle, startingResult) as T
}
throw IllegalArgumentException("Type anything useful here as exception")
}

}

Here we will make the View Model Class that uses Live Data. You see I use both Mutable Live Data and Live Data because we need to use Mutable Live Data so we can make it editable inside View Model and only readable outside View Model. That’s why we need to encapsulate it. We make the mutable live data as private to View Model Class and use another variable as Live Data to be read outside the View Model Class using method get() like this:

private var title = MutableLiveData<String>()
private var result = MutableLiveData<Int>()
//for encapsulation we need to use LiveData
val readTitle : LiveData<String> get() = title
val readResult : LiveData<Int> get() = result

Here is the full code for View Model Class:

import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel

class MainActivityViewModel(startingTitle: String, startingResult: Int):ViewModel() {
private var title = MutableLiveData<String>()
private var result = MutableLiveData<Int>()
//for encapsulation we need to use LiveData
val readTitle : LiveData<String> get() = title
val readResult : LiveData<Int> get() = result

init {
title.value = startingTitle
result.value = startingResult
}

fun setPlus(input:Int){
result.value = (result.value)?.plus(input)
}

fun setMinus(input: Int){
result.value = (result.value)?.minus(input)
}

fun setTimes(input: Int){
result.value = (result.value)?.times(input)
}

fun setDivide(input: Int){
result.value = (result.value)?.div(input)
}
}

Here is the activity_main.xml code:

<?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">

<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">


<EditText
android:id="@+id/editInput"
android:layout_width="320dp"
android:layout_height="60dp"
android:layout_marginBottom="50dp"
android:ems="10"
android:textSize="20sp"
android:inputType="number"
android:hint="Input numbers"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<TextView
android:id="@+id/textResult"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="30dp"
android:textStyle="bold"
android:textSize="35sp"
app:layout_constraintBottom_toTopOf="@+id/editInput"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />

<TextView
android:id="@+id/textTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/black"
android:textSize="25sp"
android:layout_marginBottom="20dp"
app:layout_constraintBottom_toTopOf="@+id/textResult"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />

<Button
android:id="@+id/buttonPlus"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:layout_marginTop="15dp"
android:layout_marginStart="48dp"
android:textAllCaps="false"
android:textSize="20sp"
android:text="Plus"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/editInput" />

<Button
android:id="@+id/buttonMinus"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:layout_marginTop="15dp"
android:layout_marginLeft="20dp"
android:text="Minus"
android:textAllCaps="false"
android:textSize="20sp"
app:layout_constraintStart_toEndOf="@+id/buttonPlus"
app:layout_constraintTop_toBottomOf="@+id/editInput" />

<Button
android:id="@+id/buttonTimes"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:layout_marginTop="15dp"
android:text="Times"
android:textAllCaps="false"
android:textSize="20sp"
app:layout_constraintStart_toStartOf="@+id/buttonPlus"
app:layout_constraintTop_toBottomOf="@+id/buttonPlus" />

<Button
android:id="@+id/buttonDivide"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:layout_marginTop="15dp"
android:text="Divide"
android:textAllCaps="false"
android:textSize="20sp"
app:layout_constraintStart_toStartOf="@+id/buttonMinus"
app:layout_constraintTop_toBottomOf="@+id/buttonMinus" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

Here is the MainActivity class code:

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import com.baskara.simplelivedataandmutable.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private lateinit var viewModel: MainActivityViewModel
private lateinit var viewModelFactory: MainActivityViewModelFactory

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
viewModelFactory = MainActivityViewModelFactory("Counter Result", 0)
viewModel = ViewModelProvider(this, viewModelFactory).get(MainActivityViewModel::class.java)
viewModel.readTitle.observe(this, Observer {
binding.textTitle.text = it.toString()
})
viewModel.readResult.observe(this, Observer {
binding.textResult.text = it.toString()
})

binding.apply {

buttonPlus.setOnClickListener {
viewModel.setPlus(editInput.text.toString().toInt())
}

buttonMinus.setOnClickListener {
viewModel.setMinus(editInput.text.toString().toInt())
}

buttonTimes.setOnClickListener {
viewModel.setTimes(editInput.text.toString().toInt())
}

buttonDivide.setOnClickListener {
viewModel.setDivide(editInput.text.toString().toInt())
}
}

}
}

Here is the screenshot result of the sample app:

Thank you very much for reading this post. I hope it’s useful. See you in the next post.

--

--