2021年3月12日星期五

Trying to get RecyclerView to use a diffferent dataset

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

没有评论:

发表评论