片段重建后 ViewModel 不保留数据
ViewModel does not retain data after fragment recreation
我有三个带有底部导航栏的片段应用程序,我使用 NavigationUI 进行切换。
我还有一个创建数据的视图模型(来自资产傻瓜),片段观察我用来填充回收器视图的实时数据数组列表。
我的问题是每当我切换片段时,在重新创建片段时,我不希望每次重新创建片段时都进行数据检索。因此使用了视图模型。但就我而言,视图模型中的数据没有保留。
我附上了片段和视图模型 code.I 我不确定这里有什么问题。
我已经尝试记录 aaraylist 中的条目数,如果我不调用填充数组列表的例程,它会返回 0。
歌曲片段
private const val TAG = "Songs Fragment"
class SongsFragment : Fragment(), android.widget.SearchView.OnQueryTextListener {
private val viewmodel: SongListViewModel by lazy { ViewModelProviders.of(this).get(SongListViewModel::class.java)}
private val songListAdapter = SongListAdapter(arrayListOf())
private var raga = "All"
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
Log.d(TAG, "onCreateView called")
return inflater.inflate(R.layout.fragment_songs, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
Log.d(TAG, "onViewCreated called")
val context: FragmentActivity? = activity
(activity as AppCompatActivity).supportActionBar?.subtitle = "Songs"
val assetsPath: AssetManager? = context?.assets
val assetList = assetsPath?.list("")
if (assetList != null) {
for (item in assetList)
Log.d("SongsFragment", assetsPath.toString() + item)
}
arguments?.let {
raga = SongsFragmentArgs.fromBundle(it).raga
}
Log.d("Song Fragment", "Raga passed: " + raga)
Log.d(TAG, "Number of songs: " + viewmodel.songList.size.toString())
if (raga == "All") {
viewmodel.allSongs()
viewmodel.allSongs(assetsPath!!)
} else {
viewmodel.songsForRaga(assetsPath!!, raga)
}
Log.d(TAG, ": Songlist size: " + viewmodel.songList.size.toString())
songList_RecyclerView.apply {
layoutManager = LinearLayoutManager(context)
adapter = songListAdapter
}
songSearchView.setOnQueryTextListener(this)
observeViewModel()
}
override fun onQueryTextSubmit(query: String): Boolean {
return false
}
override fun onQueryTextChange(newText: String): Boolean {
Log.i("Song Fragment", "Text change:" + newText.length.toString())
viewmodel.songSearchFilter(newText)
return false
}
fun observeViewModel() {
viewmodel.songs.observe(this, Observer { songs ->
songs?.let {
Log.d("Song Fragment", "ObserveViewModel")
songListAdapter.updateSongList(songs)
}
})
}
}
SongListViewModel
private const val TAG = "SongListViewModel"
class SongListViewModel : ViewModel() {
val songs = MutableLiveData<ArrayList<Song>>()
var songList = arrayListOf<Song>()
fun allSongs() {
songList = getAllSongs()
songs.value = ArrayList(songList.sortedWith(compareBy({ it.songName })))
}
fun allSongs(assetsPath: AssetManager) {
Log.d(TAG, "allSongs called")
Log.d(TAG, "Number of songs: " + songList.size.toString())
getAllSongs(assetsPath)
songs.value = ArrayList(songList.sortedWith(compareBy({ it.songName })))
}
fun songSearchFilter(text: String) {
var filteredList = arrayListOf<Song>()
filteredList.clear()
if (text.length != 0) {
for (song in songList) {
if (song.songName.toLowerCase().contains(text)) {
filteredList.add(song)
}
}
songs.value = ArrayList(filteredList.sortedWith(compareBy({ it.songName })))
} else {
songs.value = ArrayList(songList.sortedWith(compareBy({ it.songName })))
}
}
fun songsForRaga(assetsPath: AssetManager, raga: String) {
Log.d(TAG, "songsForRaga called")
var filteredList = arrayListOf<Song>()
filteredList.clear()
allSongs(assetsPath)
for (song in songList) {
if (song.raga == raga) {
filteredList.add(song)
}
}
songs.value = ArrayList(filteredList.sortedWith(compareBy({ it.songName })))
}
fun getAllSongs(assetsPath: AssetManager) {
Log.d(TAG, "getAllSongs called")
val bufferedReader = assetsPath.open("test.csv").bufferedReader()
val lineList = mutableListOf<String>()
bufferedReader.useLines { lines -> lines.forEach { lineList.add(it) } }
lineList.forEach {
val parts = it.split(",")
songList.add(Song(parts[0], parts[1], parts[2], parts[3], ""))
}
}
}
通过向 viewmodelprovider 传递父片段 activity 而不是片段本身来检索视图模型。
ViewModelProviders.of(activity).get(SongListViewModel::class.java)
我不认为它是一个答案,只是一点解释,你的 viewModel 生命周期与你的片段生命周期相关联,所以当你改变片段时,它会被破坏并且你的 viewModel 也被破坏。解决方案是将创建视图模型移动到 activity 并像 Nezih 建议的那样传递它,但我不确定这是最好的选择。
请注意,我们需要在 activity 范围内创建 ViewModel
实例,否则 android 将创建一个单独的实例而不是共享同一个实例,我们将无法获取数据。
对于片段,这样做:
activity?.let {
val viewmodel = ViewModelProviders.of(it).get(SongListViewModel::class.java)
viewmodel.songs.observe(this, Observer { songs ->
songs?.let {
Log.d("Song Fragment", "ObserveViewModel")
songListAdapter.updateSongList(songs)
}
})
}
我有三个带有底部导航栏的片段应用程序,我使用 NavigationUI 进行切换。 我还有一个创建数据的视图模型(来自资产傻瓜),片段观察我用来填充回收器视图的实时数据数组列表。
我的问题是每当我切换片段时,在重新创建片段时,我不希望每次重新创建片段时都进行数据检索。因此使用了视图模型。但就我而言,视图模型中的数据没有保留。 我附上了片段和视图模型 code.I 我不确定这里有什么问题。
我已经尝试记录 aaraylist 中的条目数,如果我不调用填充数组列表的例程,它会返回 0。
歌曲片段
private const val TAG = "Songs Fragment"
class SongsFragment : Fragment(), android.widget.SearchView.OnQueryTextListener {
private val viewmodel: SongListViewModel by lazy { ViewModelProviders.of(this).get(SongListViewModel::class.java)}
private val songListAdapter = SongListAdapter(arrayListOf())
private var raga = "All"
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
Log.d(TAG, "onCreateView called")
return inflater.inflate(R.layout.fragment_songs, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
Log.d(TAG, "onViewCreated called")
val context: FragmentActivity? = activity
(activity as AppCompatActivity).supportActionBar?.subtitle = "Songs"
val assetsPath: AssetManager? = context?.assets
val assetList = assetsPath?.list("")
if (assetList != null) {
for (item in assetList)
Log.d("SongsFragment", assetsPath.toString() + item)
}
arguments?.let {
raga = SongsFragmentArgs.fromBundle(it).raga
}
Log.d("Song Fragment", "Raga passed: " + raga)
Log.d(TAG, "Number of songs: " + viewmodel.songList.size.toString())
if (raga == "All") {
viewmodel.allSongs()
viewmodel.allSongs(assetsPath!!)
} else {
viewmodel.songsForRaga(assetsPath!!, raga)
}
Log.d(TAG, ": Songlist size: " + viewmodel.songList.size.toString())
songList_RecyclerView.apply {
layoutManager = LinearLayoutManager(context)
adapter = songListAdapter
}
songSearchView.setOnQueryTextListener(this)
observeViewModel()
}
override fun onQueryTextSubmit(query: String): Boolean {
return false
}
override fun onQueryTextChange(newText: String): Boolean {
Log.i("Song Fragment", "Text change:" + newText.length.toString())
viewmodel.songSearchFilter(newText)
return false
}
fun observeViewModel() {
viewmodel.songs.observe(this, Observer { songs ->
songs?.let {
Log.d("Song Fragment", "ObserveViewModel")
songListAdapter.updateSongList(songs)
}
})
}
}
SongListViewModel
private const val TAG = "SongListViewModel"
class SongListViewModel : ViewModel() {
val songs = MutableLiveData<ArrayList<Song>>()
var songList = arrayListOf<Song>()
fun allSongs() {
songList = getAllSongs()
songs.value = ArrayList(songList.sortedWith(compareBy({ it.songName })))
}
fun allSongs(assetsPath: AssetManager) {
Log.d(TAG, "allSongs called")
Log.d(TAG, "Number of songs: " + songList.size.toString())
getAllSongs(assetsPath)
songs.value = ArrayList(songList.sortedWith(compareBy({ it.songName })))
}
fun songSearchFilter(text: String) {
var filteredList = arrayListOf<Song>()
filteredList.clear()
if (text.length != 0) {
for (song in songList) {
if (song.songName.toLowerCase().contains(text)) {
filteredList.add(song)
}
}
songs.value = ArrayList(filteredList.sortedWith(compareBy({ it.songName })))
} else {
songs.value = ArrayList(songList.sortedWith(compareBy({ it.songName })))
}
}
fun songsForRaga(assetsPath: AssetManager, raga: String) {
Log.d(TAG, "songsForRaga called")
var filteredList = arrayListOf<Song>()
filteredList.clear()
allSongs(assetsPath)
for (song in songList) {
if (song.raga == raga) {
filteredList.add(song)
}
}
songs.value = ArrayList(filteredList.sortedWith(compareBy({ it.songName })))
}
fun getAllSongs(assetsPath: AssetManager) {
Log.d(TAG, "getAllSongs called")
val bufferedReader = assetsPath.open("test.csv").bufferedReader()
val lineList = mutableListOf<String>()
bufferedReader.useLines { lines -> lines.forEach { lineList.add(it) } }
lineList.forEach {
val parts = it.split(",")
songList.add(Song(parts[0], parts[1], parts[2], parts[3], ""))
}
}
}
通过向 viewmodelprovider 传递父片段 activity 而不是片段本身来检索视图模型。
ViewModelProviders.of(activity).get(SongListViewModel::class.java)
我不认为它是一个答案,只是一点解释,你的 viewModel 生命周期与你的片段生命周期相关联,所以当你改变片段时,它会被破坏并且你的 viewModel 也被破坏。解决方案是将创建视图模型移动到 activity 并像 Nezih 建议的那样传递它,但我不确定这是最好的选择。
请注意,我们需要在 activity 范围内创建 ViewModel
实例,否则 android 将创建一个单独的实例而不是共享同一个实例,我们将无法获取数据。
对于片段,这样做:
activity?.let {
val viewmodel = ViewModelProviders.of(it).get(SongListViewModel::class.java)
viewmodel.songs.observe(this, Observer { songs ->
songs?.let {
Log.d("Song Fragment", "ObserveViewModel")
songListAdapter.updateSongList(songs)
}
})
}