I have a room database where I have songs associated with artists and when I change artist from the Main Activity overflow menu, the fragment with the recyclerview showing a list of songs doesn't update unless I navigate away from the fragment and back again. I thought my observing of the list was sufficient because it worked for other changes being made but not this time.
How do I get it to update with the new artist's songs when data changes?
songViewModel
//default artist name for this phase of production
var artistName = "Ear Kitty"
private val _artistNameLive = MutableLiveData<String>(artistName)
val artistNameLive: LiveData<String>
get() = _artistNameLive
private var _allSongs : MutableLiveData<List<SongWithRatings>> = repository.getArtistSongsWithRatings(artistNameLive.value.toString()).asLiveData() as MutableLiveData<List<SongWithRatings>>
val allSongs: LiveData<List<SongWithRatings>>
get() = _allSongs
fun changeArtist(artist: String){
_artistNameLive.value = artist
artistName = artist
updateAllSongs()
}
fun updateAllSongs() = viewModelScope.launch {
run {
_allSongs = repository.getArtistSongsWithRatings(artistNameLive.value.toString())
.asLiveData() as MutableLiveData<List<SongWithRatings>>
}
}
MainFragment The observer worked fin when changes were made to all songs but not when it was updated entirely with a different artist.
songViewModel.allSongs.observe(viewLifecycleOwner) { song ->
// Update the cached copy of the songs in the adapter.
Log.d("LiveDataDebug","Main Fragment Observer called")
Log.d("LiveDataDebug",song[0].song.songTitle)
song.let { adapter.submitList(it) }
}
I find a bunch of answers saying to make a function in the Fragment that I call from Main Activity but I don't know where that function would go since I can't make the adapter a member outside of the onViewCreated. At first I'd get an error saying the adapter may have changed then I tried this below and got a null pointer exception.
MainFragment
lateinit var adapter: ItemAdapter
fun notifyThisFragment(){
adapter.notifyDataSetChanged()
}
MainActivity
songViewModel.changeArtist(artistList[which])
val navHostController = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
//I get the null pointer exception on the line below
val mainFrag: MainFragment = navHostController.childFragmentManager.findFragmentById(R.id.mainFragment) as MainFragment
mainFrag.notifyThisFragment()
As I understand it, my main activity hosts the navHostFragment which is the parent fragment to my MainFragment. Am I getting something wrong there? Is there a different way I should be doing this? I'm trying to follow the suggested architecture rather than do a bunch of weird work abounds. Am I supposed to only get allSongs from the db one time and filter it in songViewModel? I don't understand why allSongs.observe isn't getting the change.
I ended up solving the problem by making an allArtistSongsWithRatings that I use for my list adapter. I filter the list from allSongsWithRatings to avoid extra db queries. This was unsuccessful at first because I was returning null when I'd try and filter allSongsWithRatings . It turns out I needed to observe allSongsWithRatings BEFORE filtering it because of the way the LiveData works. I observe allSongsWithRatings in MainActivity and initializeArtist .
Main Activity
//initialize viewModel
songViewModel.allSongsWithRatings.observe(this){
songViewModel.initializeWithArtist()
}
Main Fragment
songViewModel.allArtistSongsWithRatings.observe(viewLifecycleOwner) { song ->
// Update the cached copy of the songs in the adapter.
Log.d("LiveDataDebug","Main Fragment Observer called")
//Log.d("LiveDataDebug",song[0].song.songTitle)
song.let { adapter.submitList(it) }
}
SongViewModel
var artistName = "Ear Kitty"
private val _artistNameLive = MutableLiveData<String>(artistName)
val artistNameLive: LiveData<String>
get() = _artistNameLive
private val _allSongsWithRatings : MutableLiveData<List<SongWithRatings>> = repository.allSongsWithRatings.asLiveData() as MutableLiveData<List<SongWithRatings>>
val allSongsWithRatings: LiveData<List<SongWithRatings>>
get() = _allSongsWithRatings
private val _allArtistSongsWithRatings = (artistNameLive.value?.let {
repository.getArtistSongsWithRatings(
it
).asLiveData()
} )as MutableLiveData<List<SongWithRatings>>
val allArtistSongsWithRatings: LiveData<List<SongWithRatings>>
get() = _allArtistSongsWithRatings
fun changeArtist(artist: String){
_artistNameLive.value = artist
artistName = artist
initializeWithArtist()
}
fun initializeWithArtist(){
var newList = mutableListOf<SongWithRatings>()
newList = allSongsWithRatings.value as MutableList<SongWithRatings>
//make sure the list isn't empty
if(newList.isNullOrEmpty()){
//handle empty list error
}else{
//list sorted by performance rating
_allArtistSongsWithRatings.value = newList.filter { it.song.artistName == artistNameLive.value } as MutableList<SongWithRatings>
allArtistSongsWithRatings.value
}
}
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.