I recently started testing the MVVM pattern in and...
# android
j
I recently started testing the MVVM pattern in android and I've spent a lot of time trying to move the seekbar functionality to my viewmodel. The problem I'm having now is that I need to update more than one value when a MutableLiveData is set, and I'm getting a circular dependency problem. I'm not even sure if this is how you're supposed to do it, but I'm overriding setValue and postValue. seekBarProgress.value cannot be set because it's not initialized.
Copy code
class AddJobViewModel() : ViewModel() {
    val minPrice = 20
    val maxPrice = 1000
    val step = 10
    var description: MutableLiveData<String> = MutableLiveData("")
    var displayPrice: MutableLiveData<String> = MutableLiveData(String.format("Price: %d Kr", minPrice))
    val seekBarMax: MutableLiveData<Int> = MutableLiveData((maxPrice - minPrice) / step)

    var currentPrice: MutableLiveData<Int>
    var seekBarProgress: MutableLiveData<Int>

    init {
        currentPrice = object: MutableLiveData<Int>(minPrice) {
            override fun setValue(value: Int?) {
                if (this.value == value) {
                    return
                }
                super.setValue(value)
                displayPrice.value = String.format("Price: %d Kr", value)
                seekBarProgress.value = value!! / step - minPrice
            }

            override fun postValue(value: Int?) {
                if (this.value == value) {
                    return
                }
                super.postValue(value)
                displayPrice.postValue(String.format("Price: %d Kr", value))
                seekBarProgress.postValue(value!! / step - minPrice)
            }
        }
        seekBarProgress = object: MutableLiveData<Int>(0) {
            override fun setValue(value: Int?) {
                if (this.value == value) {
                    return
                }
                super.setValue(value)
                currentPrice.value = minPrice + value!! * step
            }

            override fun postValue(value: Int?) {
                if (this.value == value) {
                    return
                }
                super.postValue(value)
                currentPrice.postValue(minPrice + value!! * step)
            }
        }

        currentPrice.value = 200
    }
}
So far trying to follow the MVVM pattern in android has taken a lot of time, making me doubt if it's worth doing.
setting seekBarProgress as lateinit solved the problem. I'm still interested to know of possible alternative solutions when you need to update several values. My solution is my own hack and I can't find any information about how you "should" do this kind of thing.
t
Looks completely wrong to me. You should assign LiveData members only once at construction time (use val not var) and modify their values whenever needed by calling setValue() or postValue().
In case you have dependencies between LiveData objects, you can use Transformations (e.g. map()) to transform implicitly. Observers of dependent LD will update when original one updates.
Example:
Copy code
val currentPrice = MutableLiveData<Int>()
    val displayPrice = Transformations.map(currentPrice) {
        String.format("Price: %d Kr", it)
    }
But careful: displayPrice will update for observers only. displayPrice.value will not always provide the current value. Took me a while to find this out. Reliable updes you get like
Copy code
val currentPrice = MutableLiveData<Int>()
    val displayPrice = MutableLiveData<String>()

    init {
        currentPrice.observeForever() {
            displayPrice.value = String.format("Price: %d Kr", it)
        }
    }
j
Excellent answer, thank you. I will test it out tomorrow.
I still needed to override OnChange (or possibly do it in when I observe) to solve currentPrice and seekbar infinitely setting each other.
Copy code
val seekBarMax: MutableLiveData<Int> = MutableLiveData((maxPrice - minPrice) / step)
    val currentPrice = object: MutableLiveData<Int>(minPrice) {
        override fun setValue(value: Int?) {
            if (this.value != value) {
                super.setValue(value)
            }
        }

        override fun postValue(value: Int?) {
            if (this.value != value) {
                super.postValue(value)
            }
        }
    }

    val seekBarProgress = object: MutableLiveData<Int>(0) {
        override fun setValue(value: Int?) {
            if (this.value != value) {
                super.setValue(value)
            }
        }

        override fun postValue(value: Int?) {
            if (this.value != value) {
                super.postValue(value)
            }
        }
    }

    val displayPrice = Transformations.map(currentPrice) {
        "Price: $it Kr"
    }

    init {
        seekBarProgress.observeForever {
            currentPrice.value = minPrice + it * step
        }
        currentPrice.observeForever {
            seekBarProgress.value = (it - minPrice) / step
        }
    }
But this solution looks much better now with your input.
Perhaps there's a cleaner way to do this as well? ­čÖé
one would think that they already would check for trying to set the variable to the same thing
t
Why do you want to set the value of displayPrice at all? I would set the price, that's it. No loops.