简体   繁体   中英

Android MVVM + Room creating LiveData RecyclerViewItem objects by other LiveData objects

I have Room Entity Class "Symptom" with name of Symptom and id of it.

@Entity(tableName = "symptoms")
data class Symptom(
    @PrimaryKey @NonNull val id: Int,
    val name: String) {

    override fun toString(): String {
        return "Symptom $id: $name"
    }
}

I'm getting it in the following classses:

SymptomDao

@Dao
interface SymptomDao {

    @Query("SELECT * FROM symptoms WHERE id=:id LIMIT 1")
    fun getSymptom(id: Int): Symptom

    @Query("SELECT * FROM symptoms")
    fun getAllSymptoms(): LiveData<List<Symptom>>
}

SymptomRepository

class SymptomRepository(private val symptomDao: SymptomDao) {

    fun getSymptom(id: Int) = symptomDao.getSymptom(id)

    fun getAllSymptoms() = symptomDao.getAllSymptoms()
}

SymptomsViewModel

class SymptomsViewModel(symptomRepository: SymptomRepository): ViewModel() {

    private val symptomsList = symptomRepository.getAllSymptoms()
    private val symptomsItemsList: MutableLiveData<List<SymptomItem>> = MutableLiveData()

    fun getAllSymptoms(): LiveData<List<Symptom>> {
        return symptomsList
    }

    fun getAllSymptomsItems(): LiveData<List<SymptomItem>> {
        return symptomsItemsList
    }
}

I have RecyclerView with list of SymptomItem with Checkboxes to remember which Symptoms of a list users chooses:

data class SymptomItem(
    val symptom: Symptom,
    var checked: Boolean = false)

Question

My question is how can I get LiveData<List<SymptomItem>> by LiveData<List<Symptom>> ? I have just started learning MVVM and I can't find a simply answer how to do that. I have already tried to fill this list in various ways, but It loses checked variable every time I rotate my phone. I'll be grateful for any hints.

You'll need to store which items are checked by storing their Ids in a List within the ViewModel. Then you'll have combine the list of your Symptom objects and the list of which items are checked, and generate the list of SymptomItem objects.

I'm going to use Kotlin Flow to achieve this.

@Dao
interface SymptomDao {

    @Query("SELECT * FROM symptoms")
    fun flowAllSymptoms(): Flow<List<Symptom>>
}
class SymptomRepository(private val symptomDao: SymptomDao) {

    fun flowAllSymptoms() = symptomDao.flowAllSymptoms()
}
class SymptomsViewModel(
    private val symptomRepository: SymptomRepository
) : ViewModel() {

    private val symptomsListFlow = symptomRepository.flowAllSymptoms()

    private val symptomsItemsList: MutableLiveData<List<SymptomItem>> = MutableLiveData()

    private var checkedIdsFlow = MutableStateFlow(emptyList<Int>())

    init {
        viewModelScope.launch {
            collectSymptomsItems()
        }
    }

    private suspend fun collectSymptomsItems() =
        flowSymptomsItems().collect { symptomsItems ->
            symptomsItemsList.postValue(symptomsItems)
        }

    private fun flowSymptomsItems() =
        symptomsListFlow
        .combine(checkedIdsFlow) { list, checkedIds ->
            list.map { SymptomItem(it, checkedIds.contains(it.id)) }
        }

    fun checkItem(id: Int) {
        (checkedIdsFlow.value as MutableList<Int>).add(id)
        checkedIdsFlow.value = checkedIdsFlow.value
    }

    fun uncheckItem(id: Int) {
        (checkedIdsFlow.value as MutableList<Int>).remove(id)
        checkedIdsFlow.value = checkedIdsFlow.value
    }

    fun getSymptomsItems(): LiveData<List<SymptomItem>> {
        return symptomsItemsList
    }
}

In your Fragment, observe getSymptomsItems() and update your adapter data.

The code is not tested, you may have to make small adjustments to make it compile.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM