7

I have two variables in my ViewModel which are controlled by user with buttons. He can decrement or increment value by click on proper button.

val age = MutableLiveData<Int>().apply { value = 0 }

val weight = MutableLiveData<Double>().apply { value = 0.0 }

Now I want enable save button only if value of both variables are greater than 0. How to do that? I thought about create another LiveData variable in ViewModel correctData or observe age and weight variable in Activity in some way, but I need help, because I don't know how to do it.

Thank you for help.

UPDATE

I created MediatorLiveData and it's almost working, almost because it doesn't detect if both sources give true value but if there is any true value.

private val _correctData = MediatorLiveData<Boolean>().apply {
        value = false
        addSource(age) { x -> x?.let { value = x > 0 } }
        addSource(repeats) { x -> x?.let { value = x > 0 } }
    }

Any ideas?

Remzo
  • 149
  • 3
  • 12

2 Answers2

5

Try using MediatorLiveData, it is designed to merge several LiveData into a single stream https://developer.android.com/reference/android/arch/lifecycle/MediatorLiveData

Since you're going to merge two different types, I'd recommend creating a new class to hold these values:

data class AgeAndWeight(val age: Int = 0, val weight: Double = 0.0)

Then create a variable with type MediatorLiveData:

val ageAneWeight = MediatorLiveData<AgeAndWeight>().apply { AgeAndWeight() }

Implement your merge function:

fun merge() {
        ageAneWeight.addSource(age) { value ->
            val previous = ageAneWeight.value
            ageAneWeight.value = previous?.copy(age = value)
        }
        ageAneWeight.addSource(weight) { value ->
            val previous = ageAneWeight.value
            ageAneWeight.value = previous?.copy(weight = value)
        }
    }

And observe your new live data:

fun observe() {
    ageAneWeight.observe(this, Observer {
        if (it.age != 0 && it.weight > 0.0) {
            //do whatever you want
        }
    })
}
Stanislav Shamilov
  • 1,746
  • 11
  • 20
  • I tried to create ```val correctData= MediatorLiveData().apply { value = false }``` type ```Boolean``` and add sources to it, but my age and weight variables are different type. – Remzo Jul 30 '19 at 11:52
  • Good idea, that would work, but if I would to display something depending of value much more variables then creating some artificial classes just to observe sounds stupid. Imagine yourself. I have 3 variables age, weight, growth, then if I would display something if age and weight is greater than 0 and display something else if weight and growth is greater than zero, then I have to create AgeWeightClass and WeightGrowthClass. Of course I could have one bigger class, but i think it can be done much better. Don't you think? – Remzo Jul 30 '19 at 12:36
  • @"Stanislav Shamilov" Check my updated question please – Remzo Jul 30 '19 at 12:40
  • Actually, having a single class wouldn't be so stupid, and moreover, there is an architectural principle based on that, it's called MVI. The main idea is that you have a single state of your screen which you can depend on at any time. For your case your state can be described by three variables: age, weight and growth. Grouping them together in a single data class will provide you an ability to retrieve the state of your screen at any time. So even if a configuration change has occurred, you can restore your screen from a single object (of course, this object should be saved/restored) – Stanislav Shamilov Jul 30 '19 at 12:46
  • If you didn't use a single object for keeping the state, you would have to save/restore these variables independently. Moreover, this principle makes testing really easy, because instead of writing complex tests you can just check your single state after any actions and assert it with a desired state. – Stanislav Shamilov Jul 30 '19 at 12:49
1

Use another 2 boolean variables that keep track of whether both LiveData has returned true.

private val _correctData = MediatorLiveData<Boolean>().apply {
        var weightFlag = false
        var ageFlag = false
        value = false
        addSource(age) { x -> x?.let { 
                            ageFlag = x > 0 
                            if (weightFlag && ageFlag) {
                                value = true
                            }
                        } }
        addSource(weight) { x -> x?.let { 
                            weightFlag = x > 0 
                            if (weightFlag && ageFlag) {
                                value = true
                            }
                        } }
    }

Chrisvin Jem
  • 3,940
  • 1
  • 8
  • 24