Reputation:
I have two LiveData objects of the same type and I want to do the same calculations on the output. Something like a merge operator in Rx how can I achieve this. currently I'm doing it like so:
class VM : ViewModel() {
val input1LiveData = MutableLiveData<Int>()
val input2LiveData = MutableLiveData<Int>()
val squareLiveData = MutableLiveData<Int>()
}
class MyFragment : Fragment() {
private void fillSquare(input: Int){
viewmodel.squareLiveData.value = input * input
}
override onViewCreated(){
viewmodel.input1LiveData.observe(viewLifecycleOwner){
fillSquare(it * it)
}
viewmodel.input2LiveData.observe(viewLifecycleOwner){
fillSquare(it * it)
}
}
}
Which I guess is a bad approach
Upvotes: 2
Views: 871
Reputation: 81549
The provided answer is correct, it is indeed a MediatorLiveData that can solve this issue, for example:
public class CombinedLiveData2<A, B> extends MediatorLiveData<Pair<A, B>> {
private A a;
private B b;
public CombinedLiveData2(LiveData<A> ld1, LiveData<B> ld2) {
setValue(Pair.create(a, b));
addSource(ld1, (a) -> {
if(a != null) {
this.a = a;
}
setValue(Pair.create(a, b));
});
addSource(ld2, (b) -> {
if(b != null) {
this.b = b;
}
setValue(Pair.create(a, b));
});
}
}
If you want to do this for more LiveDatas, you can use https://github.com/Zhuinden/livedata-combinetuple-kt/
class VM : ViewModel() {
val input1LiveData = MutableLiveData<Int>(0)
val input2LiveData = MutableLiveData<Int>(0)
val squareLiveData = combineTupleNonNull(input1LiveData, input2LiveData)
.map { (input1, input2) ->
/* do transform */
}
}
class MyFragment : Fragment() {
override onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewmodel.squareLiveData.observe(viewLifecycleOwner) {
// do something with square value
}
}
}
Upvotes: 1
Reputation: 3186
You can use a MediatorLiveData
which is an special type of LiveData able to connect to other liveData objects as sources. You can improve the code above like this:
class VM : ViewModel() {
val input1LiveData = MutableLiveData<Int>()
val input2LiveData = MutableLiveData<Int>()
val squareLiveData = MediatorLiveData<Int>().apply{
val observer = Observer<T> { value = it*it }
addSource(input1LiveData, observer)
addSource(input2LiveData, observer)
}
}
class MyFragment : Fragment() {
override onViewCreated(){
viewmodel.squareLiveData.observe(viewLifecycleOwner){
//do your ui updates
}
}
}
The above code will create a MediatorLiveData
objects with 2 sources and the observer will calculate the square and will set in on value.
Also you can use ReactiveLiveData library, to implement this with a reactive approach which makes the code easier to write and read :D Just like this.
class VM : ViewModel() {
val input1LiveData = MutableLiveData<Int>()
val input2LiveData = MutableLiveData<Int>()
val squareLiveData = input1LiveData.merge(input2LiveData).map{it*it}
}
class MyFragment : Fragment() {
override onViewCreated(){
viewmodel.squareLiveData.observe(viewLifecycleOwner){
//do your ui updates
}
}
}
Upvotes: 4