【问题标题】:Trying to get RecyclerView to use a diffferent dataset试图让 RecyclerView 使用不同的数据集
【发布时间】:2021-03-12 21:35:07
【问题描述】:

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

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

将 Room Database 与存储库和 viewModel 一起使用,而我一直在努力拼凑的是,拥有三个不同的列表是很好的,我可以调整到,所以在 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 编程非常陌生,我确定我的做法是错误的,但有没有办法强制刷新列表?


更新:

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


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

然后在我的主要活动中,我在处理微调器更改时更改了我的逻辑,以告诉视图模型做它的事情,然后移除观察者并将其重新打开

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)
        }
    }
}

这似乎可以让回收站视图更新,但它是 hacky 吗?

【问题讨论】:

    标签: android kotlin android-recyclerview android-room android-livedata


    【解决方案1】:

    据我所知,notifyDataSetChanged 没有做任何事情是完全有道理的,您不会在调用之前提交任何新数据。但是我认为您要做的是让适配器对viewMode 的更改做出反应。

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

    Transformations.switchMap(LiveData&lt;X&gt;, Function&lt;X, LiveData&lt;Y&gt;&gt;) 方法(或其等效的 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)
    }
    

    【讨论】:

    • 谢谢!效果非常好!我不得不将onItemSelected 更改为使用animalViewModel.modes.value = ViewModes.fromInt(position),但这是有道理的,因为你说它是伪代码
    猜你喜欢
    • 1970-01-01
    • 2016-06-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-18
    相关资源
    最近更新 更多