5 回答

TA貢獻1856條經驗 獲得超11個贊
你需要的是所謂的zip功能:
fun <A, B> zip(first: LiveData<A>, second: LiveData<B>): LiveData<Pair<A, B>> {
val mediatorLiveData = MediatorLiveData<Pair<A, B>>()
var isFirstEmitted = false
var isSecondEmitted = false
var firstValue: A? = null
var secondValue: B? = null
mediatorLiveData.addSource(first) {
isFirstEmitted = true
firstValue = it
if (isSecondEmitted) {
mediatorLiveData.value = Pair(firstValue!!, secondValue!!)
isFirstEmitted = false
isSecondEmitted = false
}
}
mediatorLiveData.addSource(second) {
isSecondEmitted = true
secondValue = it
if (isFirstEmitted) {
mediatorLiveData.value = Pair(firstValue!!, secondValue!!)
isFirstEmitted = false
isSecondEmitted = false
}
}
return mediatorLiveData
}
現在,您可以調用zip(firstLiveData,secondLiveData)并觀察它。

TA貢獻1799條經驗 獲得超8個贊
這是一個更通用的版本,它允許您觀察多個LiveData.
fun zipLiveData(vararg liveItems: LiveData<*>): LiveData<ArrayList<Any>> {
//MediatorLiveData used to merge multiple LiveDatas
return MediatorLiveData<ArrayList<Any>>().apply {
val zippedObjects = ArrayList<Any>()
liveItems.forEach {
//Add each LiveData as a source for the MediatorLiveData
addSource(it) { item ->
//Add value to list
item?.let { it1 -> zippedObjects.add(it1) }
if (zippedObjects.size == liveItems.size) {
//If all the LiveData items has returned a value, save that value in MediatorLiveData.
value = zippedObjects
//Clear the list for next time
zippedObjects.clear()
}
}
}
}
}
上面的函數不會將null值添加到列表中,假設您null也想添加值,您需要按照以下幾行做一些事情,
fun zipLiveData(vararg liveItems: LiveData<*>): LiveData<ArrayList<Any?>> {
return MediatorLiveData<ArrayList<Any?>>().apply {
val zippedObjects = ArrayList<Any?>()
liveItems.forEach {
addSource(it) { item ->
zippedObjects.add(item)
if (zippedObjects.size == liveItems.size) {
value = zippedObjects
zippedObjects.clear()
}
}
}
}
}
更新- 我剛剛意識到上述方法不保留項目的“順序” LiveData(例如,如果第二個LiveData在第一個之前發布了一個值,您將獲得值[secondLiveDataValue, firstLiveDataValue]而不是預期值[firstLiveDataValue, secondLiveDataValue])。如果您希望保留項目值的“順序”,則可以改用以下兩個函數之一LiveData。
//If you know the LiveDatas won't get null values
fun zipLiveData(vararg liveItems: LiveData<*>): LiveData<ArrayList<Any?>> {
return MediatorLiveData<ArrayList<Any?>>().apply {
var zippedObjects = arrayOfNulls<Any>(liveItems.size)
liveItems.forEachIndexed { index, liveData ->
addSource(liveData) { item ->
zippedObjects[index] = item
if (!zippedObjects.contains(null)) {
value = zippedObjects.toCollection(ArrayList())
zippedObjects = arrayOfNulls(liveItems.size)
}
}
}
}
}
//Incase your LiveDatas might have null values
fun zipLiveDataWithNull(vararg liveItems: LiveData<*>): LiveData<ArrayList<Any?>> {
return MediatorLiveData<ArrayList<Any?>>().apply {
val zippedObjects = arrayOfNulls<Any>(liveItems.size)
val zippedObjectsFlag = BooleanArray(liveItems.size)
liveItems.forEachIndexed { index, liveData ->
addSource(liveData) { item ->
zippedObjects[index] = item
zippedObjectsFlag[index] = true
if (!zippedObjectsFlag.contains(false)) {
value = zippedObjects.toCollection(ArrayList())
for(i in 0 until liveItems.size) {
zippedObjectsFlag[i] = false
}
}
}
}
}
}

TA貢獻1862條經驗 獲得超7個贊
這個擴展功能對我有用
fun <A, B> LiveData<A>.zipWith(stream: LiveData<B>): LiveData<Pair<A, B>> {
val result = MediatorLiveData<Pair<A, B>>()
result.addSource(this) { a ->
if (a != null && stream.value != null) {
result.value = Pair(a, stream.value!!)
}
}
result.addSource(stream) { b ->
if (b != null && this.value != null) {
result.value = Pair(this.value!!, b)
}
}
return result
}

TA貢獻1895條經驗 獲得超3個贊
我的解決方案有點受到 rxjava zip 運算符的啟發,
inline fun <reified I1, I2, O> biZip(inputLiveData1: LiveData<I1>, inputLiveData2: LiveData<I2>, crossinline tranform: (data1: I1, data2: I2) -> O): LiveData<O> {
var input1: I1? = null
var input2: I2? = null
val mediatorLiveData = MediatorLiveData<O>()
mediatorLiveData.addSource(inputLiveData1) {
input1 = it
if (input1 != null && input2 != null) {
mediatorLiveData.value = tranform.invoke(input1!!, input2!!)
}
}
mediatorLiveData.addSource(inputLiveData2) {
input2 = it
if (input1 != null && input2 != null) {
mediatorLiveData.value = tranform.invoke(input1!!, input2!!)
}
}
return mediatorLiveData
}
現在你可以像這樣使用它
val liveDataString = MutableLiveData<String>()
val liveDataInt = MutableLiveData<Int>()
val liveDataofTest = biZip<String, Int, Test>(liveDataString, liveDataInt) { data1:String, data2:Int ->
return@biZip Test(data1, data2)
}
測試 pojo 就像
data class Test(val name:String,value:Int)

TA貢獻1824條經驗 獲得超5個贊
您可以像擴展功能一樣直接使用它
fun <A, B, C> LiveData<A>.zip(stream: LiveData<B>, func: (source1: A?, source2: B?) -> C): LiveData<C> {
val result = MediatorLiveData<C>()
result.addSource(this) { a ->
result.setValue(func.invoke(a,stream.value))
}
result.addSource(stream) { b ->
result.setValue(func.invoke(this.value, b))
}
return result
}
添加回答
舉報