[英]How to preserve scroll position of a RecyclerView after a configuration change when using MVVM viewmodel and livedata?
READ FIRST: Apologies, it seems I have played myself.阅读第一:抱歉,看来我已经玩过自己了。 I was using RecyclerView in my xml earlier, but switched it over for CardStackView (it still uses the exact same RecyclerView adapter).
我之前在我的 xml 中使用 RecyclerView,但将其切换为CardStackView (它仍然使用完全相同的 RecyclerView 适配器)。 If I switch back to RecyclerView, the original code below works - the scroll position is saved and restored automatically on configuration change.
如果我切换回 RecyclerView,下面的原始代码有效 - 滚动位置会在配置更改时自动保存和恢复。
I'm using a MVVM viewmodel class which successfully retains list data for a RecyclerView after a configuration change.我正在使用 MVVM 视图模型类,该类在配置更改后成功保留了 RecyclerView 的列表数据。 However, the previous RecyclerView position is not restored.
但是,之前的 RecyclerView 位置没有恢复。 Is this expected behaviour?
这是预期的行为吗? What would be a good way to solve this?
什么是解决这个问题的好方法?
I saw a blog post on medium briefly mentioning you can preserve scroll position by setting the adapter data before setting said adapter on the RecyclerView.我在媒体上看到一篇博客文章,简要提到您可以通过在 RecyclerView 上设置所述适配器之前设置适配器数据来保留滚动位置。 From what I understand, after a configuration change the livedata that was being observed earlier gets a callback.
据我了解,在配置更改后,之前观察到的实时数据会收到回调。 That callback is where I set my adapter data.
该回调是我设置适配器数据的地方。 But it seems this callback happens after the onCreate() function finishes by which point my RecyclerView adapter is already set.
但似乎这个回调是在 onCreate() 函数完成之后发生的,此时我的 RecyclerView 适配器已经设置好了。
class MainActivity : AppCompatActivity() {
private val adapter = MovieAdapter()
private lateinit var viewModel: MainViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
// Create or retrieve viewmodel and observe data needed for recyclerview
viewModel = ViewModelProvider(this).get(MainViewModel::class.java)
viewModel.movies.observe(this, {
adapter.items = it
})
binding.recyclerview.adapter = adapter
// If viewmodel has no data for recyclerview, retrieve it
if (viewModel.movies.value == null) viewModel.retrieveMovies()
}
}
class MovieAdapter :
RecyclerView.Adapter<MovieAdapter.MovieViewHolder>() {
var items: List<Movie> by Delegates.observable(emptyList()) { _, _, _ ->
notifyDataSetChanged()
}
class MovieViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val binding = ItemMovieCardBinding.bind(itemView)
fun bind(item: Movie) {
with(binding) {
imagePoster.load(item.posterUrl)
textRating.text = item.rating.toString()
textDate.text = item.date
textOverview.text = item.overview
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MovieViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.item_movie_card, parent, false)
return MovieViewHolder(view)
}
override fun onBindViewHolder(holder: MovieViewHolder, position: Int) {
holder.bind(items[position])
}
override fun getItemCount() = items.size
}
class MainViewModel : ViewModel() {
private val _movies = MutableLiveData<List<Movie>>()
val movies: LiveData<List<Movie>> get() = _movies
fun retrieveMovies() {
viewModelScope.launch {
val client = ApiClient.create()
val result: Movies = withContext(Dispatchers.IO) { client.getPopularMovies() }
_movies.value = result.movies
}
}
}
Set adapter only after its items are available.只有在其项目可用后才设置适配器。
viewModel.movies.observe(this, {
adapter.items = it
binding.recyclerview.adapter = adapter
})
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.