I've been stuck trying to figure out how to update the list that my RecyclerView is showing.
What I'm trying to do is show a subset of a shown list when a spinner is changed. I have a collection of animals in my database and some have their pet
attribute set as true
and others have it set as false
.
Using Room Database with repositories and viewModels, and what I've been trying to piece together is that it's good to have three different lists that I can tune into, so in m
Repository:
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) } }
ViewModel
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") } }
In my MainActivity
I have it set up where I have an observer on these three lists and depending on which view mode is active (the spinner sets the view mode), that list is fed into the my RecyclerView's ListAdapter's 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) } } }
I am changing the mode with my spinner doing
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() }
This works fine if add or remove an animal after changing the view mode since the observers fire and the correct one is allowed to populate the adapter, but the notifyDataSetChanged()
isn't doing anything and I've been stuck on getting the adapter to update without having to add or remove from the lists
I also tried resetting the adapter in the observer but that didn't do anything either
I am extremely new to kotlin and android programming, and I'm sure that I'm going about this the wrong way, but is there a way force a list refresh?
Update: I think I may have found a found a solution but I worry that it's hacky. In my ViewModel I am replacing the contents of my allAnimals
with the filtered lists
fun showBoth() { allAnimals = repository.allAnimals.asLiveData() } fun showPets() { allAnimals = repository.pets.asLiveData() } fun showNonPets() { allAnimals = repository.nonPets.asLiveData() }
and then in my main activity I changed my logic on when handling the spinner change to tell the view model to do its thing and then to remove the observer and slap it back on
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) } } }
this seems to work to get the recycler view to update, but is it hacky?
https://stackoverflow.com/questions/66607505/trying-to-get-recyclerview-to-use-a-diffferent-dataset March 13, 2021 at 05:35AM
没有评论:
发表评论