Trigger update for a LiveData member when another LiveData is updated in the view model
E

2

6

In a word game app I share a model between an activity and a fragment:

public class MainViewModel extends AndroidViewModel {
    private LiveData<List<Game>> mGames;
    private final MutableLiveData<Game> mDisplayedGame = new MutableLiveData<>();

(please excuse the non-english text in the screenshot)

activity and fragment

The activity observes mGames being currently played by the user and updates the navigational drawer menu (see the left side of the above screenshot).

The fragment observes mDisplayedGame and displays it in a custom view (see the right side of the above screenshot).

My problem is that when the list of the games is updated at the server (and the activity receives new list of games via Websocket and stores it in the Room), I need to post an update to the fragment: "Hey, the game you are displaying was updated, redraw it!"

Is it possible to do that from within the shared view model?

I know that I could observe mGames in the fragment too and add a code there iterating through them and then finding out if the displayed game was updated at the server.

But I would prefer to do it in the MainViewModel because I have a feeling that the fragment should only observe the one game it is displaying and that's it.

TL;DR

Whenever mGames is updated in the view model via Room, I need to notify the mDisplayedGame observers too!

Ey answered 15/12, 2018 at 12:8 Comment(2)
Take a look at MediatorLiveData (there's also some Transformation methods that might suffice as well....some of them I think just wrap MediatorLiveData).Arcadian
Transformations.switchMap should workProtocol
P
5

You should use a MediatorLiveData for this.

The way it works is that

public class MainViewModel extends AndroidViewModel {
    private final LiveData<List<Game>> mGames;
    private final MutableLiveData<Game> mSelectedGame = new MutableLiveData<>();

    private final MediatorLiveData<Game> mDisplayedGame = new MediatorLiveData<>();

    {
        mDisplayedGame.addSource(mGames, (data) -> {
            // find the new value of the selected game in the list
            mSelectedGame.setValue(newSelectedGame);
        });

        mDisplayedGame.addSource(mSelectedGame, (data) -> {
            mDisplayedGame.setValue(data);
        });
    }

And then you expose mDisplayedGame as a LiveData<Game> and it should just work.

Protocol answered 17/12, 2018 at 8:19 Comment(3)
Thank you, MediatorLiveData works now for me. Do you have an idea, how to combine MediatorLiveData with Room calls, is it even possible? If I call DAO methods from addSource, should I wrap them in Executors.newSingleThreadScheduledExecutor().execute(...)?Ey
Not sure why I didn't answer this earlier, but yes, you do want to move DB queries to a background thread. You can use postValue to come back from them. Beware that you might have started a query, then received a new event emission, in which case you might want to ignore the results of the previously started query.Protocol
You want to keep the executor as a field reference though, instead of creating one each time.Protocol
H
-3

Use callback bro -add a callback interface in your viewmodel and a setCallback method -make your fragment implement it then call viewmodel.setCallback(fragment) -call the callback in your obsever

Hamlani answered 15/12, 2018 at 14:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.