MVVM on Android

Sep 1 2022 · Kotlin 1.6, Android 12, Android Studio Chipmunk | 2021.2.1 Patch 1

Part 1: MVVM on Android

06. Build the ViewModel

Episode complete

Play next episode

Next
About this episode
Leave a rating/review
See forum comments
Cinema mode Mark complete Download course materials
Previous episode: 05. Explore the View Next episode: 07. Test the ViewModel

Get immediate access to this and 4,000+ other videos and books.

Take your career further with a Kodeco Personal Plan. With unlimited access to over 40+ books and 4,000+ professional videos in a single subscription, it's simply the best investment you can make in your development career.

Learn more Already a subscriber? Sign in.

Notes: 06. Build the ViewModel

The student materials have been reviewed and are updated as of July 2022.

Transcript: 06. Build the ViewModel

With our Model and View in place, we’ll now turn to creating a ViewModel for the Add Creature screen named CreatureViewModel.

First, create a new package in the project named viewmodel. Next, create a CreatureViewModel class in the package, and have it extend the ViewModel Android Architecture Component class.

class CreatureViewModel : ViewModel() {
 
  

}

The ViewModel will need access to a CreatureGenerator, so add a CreatureGenerator property to the constructor and assign it a default value.

class CreatureViewModel(private val generator: CreatureGenerator = CreatureGenerator()) : ViewModel() {
 
  

}

The ViewModel will use LiveData to send generated creatures to the View layer, so add a creatureLiveData property and an associated getter function.

  private val creatureLiveData = MutableLiveData<Creature>()
  
  fun getCreatureLiveData(): LiveData<Creature> = creatureLiveData

The ViewModel needs to keep track of the selected name and other properties for the creature being created, so add properties for each of those now, using type inference to set the types.

  var name = ""
  var intelligence = 0
  var strength = 0
  var endurance = 0
  var drawable = 0

Also add a lateinit property for the Creature being created.

lateinit var creature: Creature

Now add a function updateCreatures() to set the creature value for the view model, and post it to the LiveData for the creature.

  fun updateCreature() {
    val attributes = CreatureAttributes(intelligence, strength, endurance)
    creature = generator.generateCreature(attributes, name, drawable)
    creatureLiveData.postValue(creature)
  }

Next add a method that the View layer will call when the user selects a value in the creature attribute drop downs.

  fun attributeSelected(attributeType: AttributeType, position: Int) {
    when (attributeType) {
      AttributeType.INTELLIGENCE ->
      AttributeType.STRENGTH ->
      AttributeType.ENDURANCE ->
    }
    updateCreature()
  }

When an attribute is selected, we also call the updateCreature method, which will pass the new creature to the View along with it’s selected attribute in the LiveData. Add a similar function for the view layer to call when the creature avatar is selected.

  fun drawableSelected(drawable: Int) {
    this.drawable = drawable
    updateCreature()
  }

Now head over the the View layer, CreatureActivity. Add a lateinit property for the viewmodel.

private lateinit var viewModel: CreatureViewModel

When working with Architecture Component view models, you use ViewModelProviders to connect to the viewmodel, so add that call into onCreate()

viewModel = ViewModelProviders.of(this).get(CreatureViewModel::class.java)

Next, replace the TODOs in the configureSpinnerListeners methods with calls to the viewmodel method we just added.

viewModel.attributeSelected(AttributeType.INTELLIGENCE, position)
...
viewModel.attributeSelected(AttributeType.STRENGTH, position)
...
viewModel.attributeSelected(AttributeType.ENDURANCE, position)

In the text changed listener for the name edit text, replace the todo with a call to update the viewmodel name value.

viewModel.name = s.toString()

In the avatarClicked override, set the value of the viewmodel drawable using drawableSelected.

viewModel.drawableSelected(avatar.drawable)

Back up in configureUI(), add a call to hideTapLabel() if the drawable value on the viewmodel is non-zero.

if (viewModel.drawable != 0) hideTapLabel()

Next we need to handle events being sent over the LiveData from the ViewModel. Add a new method configureLiveDataObservers() and an observer of creatureLiveData from the viewmodel.

  private fun configureLiveDataObservers() {
    viewModel.getCreatureLiveData().observe(this, Observer { creature ->

    })
  }

Inside the observer, update the hitPoints value for the creature, and the avatar image and name views.

    
      hitPoints.text = creature.hitPoints.toString()
        avatarImageView.setImageResource(creature.drawable)
        nameEditText.setText(creature.name)
     

We need to wrap these in a let expression since the creature passed to the observer is nullable.

creature?.let {
    ...
}

Now add a call to configureLiveDataObservers() in onCreate():

configureLiveDataObservers()

We can build and run the app and create a new creature. Notice that when we select attributes in the drop downs, the hitpoints value is immediately updated using the value from the viewmodel.