【问题标题】:LiveData with multiple sources of different types具有多个不同类型来源的 LiveData
【发布时间】:2019-08-31 10:33:48
【问题描述】:

我目前有一个项目,其中包含 MyItem 列表,并使用 Firebase/LiveData。它分为组,每个组都有项目。

如果发生以下任何情况,我希望能够更新此列表:

  1. 项目已更新(通过 Firebase 在后端)
  2. 过滤器已更改(Firebase 上为每个用户创建一个单独的表)
  3. 已为项目添加了书签(Firebase 上为每个用户提供了一个单独的表格)

为了获取内容列表,我有一个类似这样的函数来返回 LiveData,它会在项目更新时更新(#1)。

数据库

getList(id: String): LiveData<List<MyItem>> {
    val data = MutableLiveData<List<MyItem>>()

    firestore
        .collection("groups")
        .document(id)
        .collection("items")
            .addSnapshotListener { snapshot, exception ->
                val items = snapshot?.toObjects(MyItem::class.java) ?: emptyList()

                // filter items 

                data.postValue(items)
        }

    return data
}

在我的 ViewModel 中,我有处理这种情况的逻辑。

视图模型

private val result = MediatorLiveData<Resource<List<MyItem>>>()

private var source: LiveData<List<MyItem>>? = null
val contents: LiveData<Resource<List<MyItem>>>
    get() {
        val group = database.group

        // if the selected group is changed.
        return Transformations.switchMap(group) { id ->
            // showing loading indicator
            result.value = Resource.loading(null)

            if (id != null) {
                // only 1 source for the current group
                source?.let {
                    result.removeSource(it)
                }

                source = database.getList(id).also {
                    result.addSource(it) {
                        result.value = Resource.success(it)
                    }
                }

                // how to add in source of filter changes?

            } else {
                result.value = Resource.init(null)
            }
            return@switchMap result
        }
    }

逻辑相当复杂,难以理解。有没有更好的方法来构建它来处理多个不同的变化?存储用户当前过滤器的最佳方式是什么?

谢谢。

【问题讨论】:

    标签: android kotlin google-cloud-firestore android-livedata mutablelivedata


    【解决方案1】:

    contents 的实现包括对外部变量的多个引用,这使得很难跟踪和跟踪状态。我只是将参考资料尽可能地保留在本地,并相信switchMap(liveData) 会做适当的工作。下面的代码应该和你的一样:

    val contents = Transformations.switchMap(database.group) { id ->
        val data = MediatorLiveData<Resource<List<MyItem>>()
    
        if (id == null) {
            data.value = Resource.init(null)
        } else {
            data.value = Resource.loading(null)
            data.addSource(database.getList(id)) {
                data.value = Resource.success(it)
            }
        }
    
        return liveData
    }
    

    关于getList(id),您可能还想正确处理exception

    【讨论】:

      【解决方案2】:

      我不知道我是否正确地回答了您的问题,但如果您的观点适用于一个列表(类似于 MyItemList)并且此列表在多种情况下更新或更改,您必须使用 @ 987654322@.

      我的意思是你必须有三个LiveData,每个负责一个情况,一个 MediatorLiveData 通知他们每个人都发生了变化。

      见下文:

      数据库

      fun getListFromServer(id: String): LiveData<List<MyItem>> {
          val dataFromServer = MutableLiveData<List<MyItem>>()
      
          firestore
            .collection("groups")
            .document(id)
            .collection("items")
                .addSnapshotListener { snapshot, exception ->
                    val items = snapshot?.toObjects(MyItem::class.java) ?: emptyList()
                    dataFromServer.postValue(items)
            }
      
          return dataFromServer
      }
      
      fun getFilteredData(id: String): LiveData<FilterData> {
          return DAO.user.getFilteredData(id)
      }
      
      fun getBookmarkedList(id: String): LiveData<BookmarkData> {
          return DAO.user.getBookmarkedData(id)
      }
      

      viewModel 中有一个MediatorLiveData 观察这些liveDatas,直到有任何数据更改通知视图。

      视图模型

      private val result = MediatorLiveData<<List<MyItem>>()
      
      fun observeOnData(id: String, owner: LifeCycleOwner, observer: Observer<List<MyItem>>) {
         result.observe(owner, observer);
      
         result.addSource(Database.getListFromServer(id), MyItemList -> {
              if(MyItemList != null)
                  result.setValue(MyItemList)
         });
         result.addSource(Database.getFilteredData(id), filterData -> {
              if(filterData != null) {
                  val myItemList = result.getValue()
                  if (myItemList == null) return
      
                  //here add logic for update myItemList depend On filterData
      
                  result.setValue(myItemList)
              }
         });
         result.addSource(Database.getBookmarkedList(id), bookmarkData -> {
              if(MyItemList != null) {
                  val myItemList = result.getValue()
                  if (myItemList == null) return
      
                  //here add logic for update myItemList depend On bookmarkData
      
                  result.setValue(myItemList)
              }
         });
      
      }
      

      【讨论】:

      • getFilteredData()getBookmarkedList() 不返回MyItem 的列表,它们返回不同的对象,例如要搜索的Filter,所以我不能简单地设置值在ViewModel 中的result。我想我需要缓存来自getListFromServer() 的整个列表,或者我需要在获得新的Filter 后再次调用。
      • @Advice-Dog 在addSource 部分中,您可以添加自定义列表的任何逻辑。查看我编辑的答案。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-05-26
      • 2016-02-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-08-26
      相关资源
      最近更新 更多