试图让 RecyclerView 使用不同的数据集

Trying to get RecyclerView to use a diffferent dataset

我一直在努力弄清楚如何更新我的 RecyclerView 显示的列表。

我想做的是在更改微调器时显示已显示列表的子集。我的数据库中有一组动物,有些动物的 pet 属性设置为 true,其他动物的属性设置为 false.

将 Room Database 与存储库和 viewModels 结合使用,我一直在努力拼凑的是,拥有三个我可以调整的不同列表是很好的,所以在 m

存储库:

class AnimalRepository(private val animalDao: AnimalDao) {
    val allAnimals: Flow<List<Animal>> = animalDao.getAnimalsByCategory()
    val pets: Flow<List<Animal>> = animalDao.getAnimalsByPetStatus(true)
    val nonPets: Flow<List<Animal>> = animalDao.getAnimalsByPetStatus(false)

    @Suppress("RedundantSuspendModifier")
    @WorkerThread
    suspend fun insert(animal: Animal) {
        animalDao.insert(animal)
    }

    @WorkerThread
    suspend fun get(id: Int): Animal {
        return animalDao.get(id)
    }

    @WorkerThread
    suspend fun delete(id: Int) {
        animalDao.delete(id)
    }
}

视图模型

class AnimalViewModel(private val repository: AnimalRepository) : ViewModel() {

    var allAnimals: LiveData<List<Animal>> = repository.allAnimals.asLiveData()
    val pets: LiveData<List<Animal>> = repository.pets.asLiveData()
    val nonPets: LiveData<List<Animal>> = repository.nonPets.asLiveData()
    var result: MutableLiveData<Animal> = MutableLiveData<Animal>()
    var mode: VIEW_MODES = VIEW_MODES.BOTH

    /*
    * Launching a new coroutine to insert the data in a non-blocking way
    * */
    fun insert(animal: Animal) = viewModelScope.launch {
        repository.insert(animal)
    }

    /*
    * Launching a new coroutine to get the data in a non-blocking way
    * */
    fun get(id: Int) = viewModelScope.launch {
        result.value = repository.get(id)
    }

    fun delete(id: Int) = viewModelScope.launch {
        repository.delete(id)
    }
}

class AnimalViewModelFactory(private val repository: AnimalRepository) : ViewModelProvider.Factory {
    override fun <T: ViewModel> create(modelClass: Class<T>): T {
        if (modelClass.isAssignableFrom(AnimalViewModel::class.java)) {
            @Suppress("UNCHECKED_CAST")
            return AnimalViewModel(repository) as T
        }
        throw IllegalArgumentException("Unknown ViewModel class")
    }
}

在我的 MainActivity 中,我将其设置为在这三个列表中有一个观察者,并根据哪个视图模式处于活动状态(微调器设置视图模式),将该列表输入到我的RecyclerView 的 ListAdapter 的 submitList

animalViewModel.allAnimals.observe(this) { animals ->
    if (viewMode == VIEW_MODES.BOTH) {
        animals.let {
            adapter.submitList(it)
            // recyclerView.adapter = adapter
        }
    }
}
animalViewModel.pets.observe(this) { animals ->
    if (viewMode == VIEW_MODES.PETS) {
        animals.let {
            adapter.submitList(it)
            // recyclerView.adapter = adapter
        }
    }
}
animalViewModel.nonPets.observe(this) { animals ->
    if (viewMode == VIEW_MODES.NON_PETS) {
        animals.let {
            adapter.submitList(it)
        }
    }
}

我正在用微调器改变模式

override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
    when (position) {
        0 -> {
            viewMode = VIEW_MODES.BOTH
        }
        1 -> {
            viewMode = VIEW_MODES.PETS
        }
        2 -> {
            viewMode = VIEW_MODES.NON_PETS
        }
    }
    adapter.notifyDataSetChanged()
}

如果在更改视图模式后添加或删除动物,这会正常工作,因为观察者开火并且允许正确的动物填充适配器,但 notifyDataSetChanged() 没有做任何事情,我已经一直坚持让适配器更新而不必从列表中添加或删除

我也尝试在观察器中重置适配器,但也没有任何作用

我对 kotlin 和 android 编程非常陌生,我确定我这样做是错误的,但是有没有办法强制刷新列表?


更新:

我想我可能已经找到了一个解决方案,但我担心它是 hacky。在我的 ViewModel 中,我将 allAnimals 的内容替换为过滤后的列表


    fun showBoth() {
        allAnimals = repository.allAnimals.asLiveData()
    }
    fun showPets() {
        allAnimals = repository.pets.asLiveData()
    }
    fun showNonPets() {
        allAnimals = repository.nonPets.asLiveData()
    }

然后在我的主要 activity 中,我更改了处理微调器更改时的逻辑,告诉视图模型执行它的操作,然后移除观察器并将其重新打开

override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
    when (position) {
        0 -> {
            animalViewModel.showBoth()
        }
        1 -> {
            animalViewModel.showPets()
        }
        2 -> {
            animalViewModel.showNonPets()
        }
    }
    refreshObserver()
}

private fun refreshObserver() {
    animalViewModel.allAnimals.removeObservers(this)
    animalViewModel.allAnimals.observe(this) { animals ->
        animals.let {
            adapter.submitList(it)
        }
    }
}

这似乎可以让回收站视图更新,但它有问题吗?

据我所知,notifyDataSetChanged 没有做任何事情是完全合理的,您在调用之前没有提交任何新数据。但是我认为你要做的是让适配器对 viewMode.

中的变化做出反应

如果是这种情况,我建议也将您的 viewMode 作为一个 LiveData 对象,然后公开一个列表供您的适配器观察,该列表会根据 viewMode 已选中。

Transformations.switchMap(LiveData<X>, Function<X, LiveData<Y>>) 方法(或其等效的 Kotlin 扩展函数)可能会在这里为您完成大部分工作。总之,它将一个 LiveData 的值映射到另一个。因此,在您的示例中,您可以将 viewMode 映射到 allAnimalspetsnonPets.

之一

为了清楚起见,这里有一个简单的伪代码概述:

AnimalViewModel {

    val allAnimals: LiveData<List<Animal>>
    val pets: LiveData<List<Animal>>
    val nonPets: LiveData<List<Animal>>

    val modes: MutableLiveData<VIEW_MODES>

    val listAnimals = modes.switchMap {
        when (it) {
            VIEW_MODES.BOTH -> allAnimals
            ...
        }
    }
}
fun onItemSelected {
    
    viewModel.onModeChanged(position)
}
viewModel.listAnimals.observe {

    adapter.submitList(it)
}