【问题标题】:Trigger update for a LiveData member when another LiveData is updated in the view model在视图模型中更新另一个 LiveData 时触发 LiveData 成员的更新
【发布时间】:2019-05-16 11:04:03
【问题描述】:

在文字游戏应用程序中,我在活动和片段之间共享模型:

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

(请原谅截图中的非英文文字)

活动观察到用户当前正在播放的mGames,并更新导航抽屉菜单(见上图左侧)。

片段观察mDisplayedGame并将其显示在自定义视图中(见上图右侧)。

我的问题是当游戏列表在服务器上更新时(并且活动通过 Websocket 接收新的游戏列表并将其存储在房间中),我需要发布更新到片段:“嘿,您正在显示的游戏已更新,重新绘制它!”

是否可以在共享视图模型中做到这一点?

我知道我也可以在片段中观察mGames,并在其中添加一个代码迭代它们,然后找出显示的游戏是否在服务器上更新。

但我更愿意在MainViewModel 中执行此操作,因为我觉得片段应该只观察它正在显示的一个游戏,仅此而已。

TL;DR

每当 mGames 通过 Room 在视图模型中更新时,我也需要通知 mDisplayedGame 观察者!

【问题讨论】:

  • 看看MediatorLiveData (还有一些Transformation 方法可能也足够了......其中一些我认为只是包装MediatorLiveData)。
  • Transformations.switchMap 应该可以工作

标签: android viewmodel observer-pattern android-architecture-components android-livedata


【解决方案1】:

使用回调兄弟 - 在视图模型中添加回调接口和 setCallback 方法 - 让你的片段实现它然后调用 viewmodel.setCallback(fragment) - 在你的观察者中调用回调

【讨论】:

    【解决方案2】:

    您应该为此使用 MediatorLiveData。

    它的工作方式是这样的

    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);
            });
        }
    

    然后您将mDisplayedGame 公开为LiveData&lt;Game&gt;,它应该可以正常工作。

    【讨论】:

    • 谢谢,MediatorLiveData 现在为我工作。你有什么想法,如何combine MediatorLiveData with Room calls,这甚至可能吗?如果我从addSource 调用DAO 方法,我应该将它们包装在Executors.newSingleThreadScheduledExecutor().execute(...) 中吗?
    • 不知道为什么我之前没有回答这个问题,但是是的,您确实想将数据库查询移动到后台线程。您可以使用postValue 从他们那里回来。请注意,您可能已经启动了一个查询,然后收到了一个新的事件发射,在这种情况下,您可能希望忽略之前启动的查询的结果。
    • 您希望将执行程序保留为字段引用,而不是每次都创建一个。
    猜你喜欢
    • 2020-09-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-19
    • 1970-01-01
    • 2021-12-27
    相关资源
    最近更新 更多