При разбивке на страницы с комнатой не удается правильно объединить поток <PagingData <Model>> с другими потоками

Я пытаюсь использовать библиотеку, чтобы получить от Room, затем проверьте, был ли выбран элемент в recyclerview, поэтому я сопоставляю этот класс с другим классом с именем. Для достижения этого сопоставления всякий раз, когда пользователь отмечал элемент как выбранный, я обновлялся внутри MutableStateFlow<Map<Int, State>>. Здесь смотрит вверх Index(Int) получить, это просто enum класс для представления государства UNINITIALISED, USELECTED а также SELECTED.

Я устанавливаю для карты значение. Проблема в том, что я пытаюсь объединить Flow<PagingData<Scan>> с, чтобы также передать как параметр в ScanMapper класс с этого State взято из StateFlow<Map<Int,State>> и не является частью оригинала Scanкласс. Но PagingDataAdapter кажется, всегда получает State UNINITIALISED несмотря на то, что я помечаю элемент как выбранный при щелчке по элементу с помощью markSelected(scanId: Int) функция.

Пожалуйста, скажите мне, что мне здесь не хватает.

ОБНОВИТЬ

Я смог достичь желаемой функциональности, используя Flow<List<Scan>> и устранение использования Paging 3 библиотека с использованием адаптера ресайклера с DiffUtils. Хотя это не реальное решение, поскольку оно устранило pagination using paging 3 библиотека, но следующие изменения позволили мне выполнить выбор элемента:

Обновленный Дао

      @Query("SELECT * FROM scan ORDER BY timeOfStorage DESC")
fun getAllScansList(): Flow<List<Scan>>

Обновленная функция в ViewModel

      fun getData(context: Context): Flow<List<ScanMapper>> {

    val dao = ScanDatabase.invoke(context).getScanDao()

    return combine(
        dao.getAllScansList(),
        myMap
    ) { scan, stateMap ->
        scan.map {
            it.toScanMapper(stateMap[it.id] ?: State.UNKNOWN)
        }
    }.flatMapLatest {
        flow {
            emit(it)
        }
    }
}

Обновлен код фрагмента

      mainViewModel.getData(requireContext()).collect {

    adapter.asyncListDiffer.submitList(it as MutableList)
}

Исходный вопрос

Мой класс сканирования:

      @Entity(tableName = "scan")
@Parcelize
data class Scan (
    @PrimaryKey(autoGenerate = true)
    var id: Int = 0,
    val name: String,
    val recognisedText: String,
    val timeOfStorage: Long,
    val filename: String
): Parcelable {

}

Мой класс ScanMapper:

      data class ScanMapper(
    val id: Int,
    val name: String,
    val recognisedText: String,
    val timeOfStorage: Long,
    val filename: String,
    val isSelected: State
)

enum class State{
    UNINITIALISED,
    SELECTED,
    UNSELECTED,
    UNKNOWN
}

Конвертер для сканирования в ScanMapper и наоборот

      fun ScanMapper.mapToScan() =
Scan(
    id = id,
    name = name,
    recognisedText = recognisedText,
    timeOfStorage = timeOfStorage,
    filename = filename
)

fun Scan.toScanMapper(isSelected: State) =
ScanMapper(
    id = id,
    name = name,
    recognisedText = recognisedText,
    timeOfStorage = timeOfStorage,
    filename = filename,
    isSelected = isSelected
)

Мой источник подкачки внутри Дао

      @Query("SELECT * FROM scan ORDER BY timeOfStorage DESC")
fun getAllScans(): PagingSource<Int,Scan>

@Query("SELECT id FROM scan")
fun getAllIds(): List<Int>

Получение потока с помощью getScansFlow()

      fun getScansFlow(context: Context): Flow<PagingData<Scan>> {

    val scanDao = ScanDatabase.invoke(context).getScanDao()

    return Pager(
        config = PagingConfig(
            pageSize = 10,
            maxSize = 30,
            enablePlaceholders = false
        ),
        pagingSourceFactory = { scanDao.getAllScans() }
    ).flow.cachedIn(viewModelScope)

}

Инициализация карты для идентификаторов, присутствующих в комнате

      fun initList() =
       viewModelScope.launch {

    var idList: List<Int> = listOf()

    withContext(Dispatchers.IO) {

        val task = async {
            return@async ScanDatabase.invoke(context).getScanDao().getAllIds()
        }
        idList = task.await()
    }

    val map = _myMap.value.toMutableMap()

    for (id in idList) {
        map[id] = State.UNINITIALISED
    }

    _myMap.value = map

}

Функция в ViewModel, которая получает поток и объединяет их

          fun getScansSyncFlow(context: Context) {

    viewModelScope.launch {

        combine(
            getScansFlow(context),
            myMap
        ) { scan, stateMap ->
            scan.map {
                it.toScanMapper(stateMap[it.id] ?: State.UNKNOWN)
            }
        }.collect{
           _myFlow.emit(it) 
        }
    }
  }

это StateFlow<PagingData<ScanMapper>?>.

Вот как я обновляю Map когда элемент выбирается пользователем в recyclerview.

      fun markSelected(scanId: Int) {

    val map = _myMap.value.toMutableMap()
    map[scanId] = State.SELECTED
    _myMap.value = map
}

Тогда я собираю это myFlow в моем fragment как это:

      lifecycleScope.launchWhenStarted {

mainViewModel.getScansSyncFlow(requireContext())

mainViewModel.myFlow.collect { data ->

      data?.let {
        
          adapter.submitData(it)
       }
    }
}

1 ответ

Решение

Непосредственная проблема, которую я вижу, заключается в том, что вы используете collect вместо collectLatest. Поскольку submitData не возвращается, вы никогда не будете получать обновления из вашего Flow.

      mainViewModel.myFlow.collectLatest {
  adapter.submitData(it)
}
Другие вопросы по тегам