LiveData с несколькими источниками разных типов

В настоящее время у меня есть проект, который содержит список 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
        }
    }

Логика довольно сложная, и ей трудно следовать. Есть ли лучший способ структурировать это для обработки нескольких различных изменений? Как лучше всего хранить текущие фильтры пользователя?

Спасибо.

11
0
8 737
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Я не знаю, правильно ли я понимаю ваш вопрос или нет, но если у вас есть представление, которое работает с одним списком (что-то вроде MyItemList), и этот список обновляется или изменяется несколькими ситуациями, вы должны работать с MediatorLiveData.

Я имею в виду, что у вас должно быть три 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, который наблюдает за этими liveData, пока какие-либо данные не изменятся, не уведомите представление.

видМодель

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 для поиска, поэтому я не могу просто установить значение result в ViewModel. Я думаю, мне нужно либо кэшировать весь список из getListFromServer(), либо мне нужно снова сделать этот вызов после получения нового Filter.

Advice-Dog 16.04.2019 07:14

@Advice-Dog в разделе addSource вы можете добавить любую логику, которая настраивает ваш список. см. мой отредактированный ответ.

SamiAzar 16.04.2019 09:26

Ваша реализация 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.

Другие вопросы по теме