How to update UI from model business logic?

Use case: a simple billing app.

When quantity or price change, the total should change.

I'm using DataBinding, how do I notify the UI that total changed?

Model:

class Detail(){
    private var _quantity = BigDecimal.ZERO
    private var _price = BigDecimal.ZERO
    private var _total = BigDecimal.ZERO

    var quantity : BigDecimal
    get() = _quantity
    set(value){
        _quantity = value
        compute()
    }

    //same boilerplate for price

   private fun compute(){
       total = price.mutiply(quantity)
   }
}

ViewModel

    var instance = Detail()

    fun addQuantity(){
         instance.quantity.add(1)
    }
    
    fun addPrice(){
         instance.price.add(1)
    }

View

    <button onClick="@{() -> viewModel.addQuantity()}"
    <button onClick="@{() -> viewModel.addPrice()}"
    <textview text="@{viewModel.instance.total}"/>

My first idea was to make a LiveData, but I couldn't find any documentation that tells it's a good idea to create LiveData properties in the Model. As far as I've read they should be in the ViewModel exclusively.

I tried it anyway, but found another problem. I could only make it responsive to 1 property, but it needs to response to 2 properties (both qty and price)

var quantity = MutableLiveData<BigDecimal>()
var price = MutableLiveData<BigDecimal>()

var total = Transformations.map(quantity){ ... } 
var total = Transformation.map(price){ ...} // Duplicate property!

What's the right approach?

In .net world you just invoked NotifyPropetyChanged() and the UI would know. I can't find an analogous logic in Android.

2 answers

  • answered 2020-09-14 05:41 raggedycoder

    I think you can follow the guideline from the codelabs by google for data binding.

    https://codelabs.developers.google.com/codelabs/android-databinding

    In shorts, you can use the LiveData for updating the view. In this way in your viewmodel should be like this

    private val _quantity = MutableLiveData("100")
    val quantity = _quantity
    

    the xml layout

    <data>
       <variable
          name="viewmodel"
          type="myviewmodel.SimpleViewModel"/>
    </data>
    
    <TextView
       android:id="@+id/quantity_text"
       android:text="@{viewmodel.quantity}"
    />
    

    Add this to the activity level

    binding.lifecycleOwner = this
    

    Just follow the link example for the much more details

  • answered 2020-09-14 05:56 Manohar Reddy

    In viewModel take instance as LiveData

    val instance = MutableLiveData<Detail>()
    
    fun addQuantity(){
         instance.value.quantity.add(1)
    }
    
    fun addPrice(){
         instance.value.price.add(1)
    }
    

    In Fragment/Activity make sure you add this line for binding

    binding.lifecycleOwner = viewLifecycleOwner
    

    make sure you have this library to use viewLifecycleOwner

     implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.2.0'