问题
I have a value in the UI that it's value depends on two LiveData objects. Imagine a shop where you need a subtotal = sum of all items price
and a total = subtotal + shipment price
. Using Transformations we can do the following for the subtotal LiveData object (as it only depends on itemsLiveData
):
val itemsLiveData: LiveData<List<Items>> = ...
val subtotalLiveData = Transformations.map(itemsLiveData) {
items ->
getSubtotalPrice(items)
}
In the case of the total it would be great to be able to do something like this:
val shipPriceLiveData: LiveData<Int> = ...
val totalLiveData = Transformations.map(itemsLiveData, shipPriceLiveData) {
items, price ->
getSubtotalPrice(items) + price
}
But, unfortunately, that's not possible because we cannot put more than one argument in the map function. Anyone knows a good way of achieving this?
回答1:
At the end I used MediatorLiveData to achieve the same objective.
fun mapBasketTotal(source1: LiveData<List<Item>>, source2: LiveData<ShipPrice>): LiveData<String> {
val result = MediatorLiveData<String>()
uiThread {
var subtotal: Int = 0
var shipPrice: Int = 0
fun sumAndFormat(){ result.value = format(subtotal + shipPrice)}
result.addSource(source1, { items ->
if (items != null) {
subtotal = getSubtotalPrice(items)
sumAndFormat()
}
})
result.addSource(source2, { price ->
if (price != null) {
shipPrice = price
sumAndFormat()
}
})
}
return result
}
回答2:
I come up with another solution.
class PairLiveData<A, B>(first: LiveData<A>, second: LiveData<B>) : MediatorLiveData<Pair<A?, B?>>() {
init {
addSource(first) { value = it to second.value }
addSource(second) { value = first.value to it }
}
}
class TripleLiveData<A, B, C>(first: LiveData<A>, second: LiveData<B>, third: LiveData<C>) : MediatorLiveData<Triple<A?, B?, C?>>() {
init {
addSource(first) { value = Triple(it, second.value, third.value) }
addSource(second) { value = Triple(first.value, it, third.value) }
addSource(third) { value = Triple(first.value, second.value, it) }
}
}
fun <A, B> LiveData<A>.combine(other: LiveData<B>): PairLiveData<A, B> {
return PairLiveData(this, other)
}
fun <A, B, C> LiveData<A>.combine(second: LiveData<B>, third: LiveData<C>): TripleLiveData<A, B, C> {
return TripleLiveData(this, second, third)
}
Then, you can combine multiple source.
val totalLiveData = Transformations.map(itemsLiveData.combine(shipPriceLiveData)) {
// Do your stuff
}
If you want to have 4 or more sources, you need to create you own data class because Kotlin only has Pair
and Triple
.
In my opinion, there is no reason to run with uiThread
in Damia's solution.
回答3:
You can use switchMap() for such case, because it returns LiveData object which can be Transformations.map()
In below code I am getting sum of final amount of two objects onwardSelectQuote and returnSelectQuote
finalAmount = Transformations.switchMap(onwardSelectQuote) { data1 ->
Transformations.map(returnSelectQuote) { data2 -> ViewUtils.formatRupee((data1.finalAmount!!.toFloat() + data2.finalAmount!!.toFloat()).toString())
}
}
来源:https://stackoverflow.com/questions/47572913/livedata-transformations-map-with-multiple-arguments