I've been experimenting with PagedListAdapter
and can't figure out how to restore adapters position correctly.
Last attempt was to save lastKey
from current list.
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
val lastKey = adapter.currentList?.lastKey as Int
outState.putInt("lastKey", lastKey)
}
but when restoring my adapter and passing lastKey
to PagedListBuilder
what I last saw and what is being displayed differs by quite a bit.
val dataSourceFactory = dao.reportsDataSourceFactory()
val builder = RxPagedListBuilder(
dataSourceFactory,
PagedList.Config.Builder()
.setEnablePlaceholders(false)
.setInitialLoadSizeHint(60)
.setPageSize(20)
.setPrefetchDistance(60)
.build()
)
.setInitialLoadKey(initialLoadKey)
.setBoundaryCallback(boundaryCallback)
If I'm in the middle of page #4 when resuming - adapter will be at position at the beginning of page #4. Ideally adapter should be restored in exactly the same position as last seen.
Various attempts to save LayoutManager state
outState.putParcelable("layout_manager_state", recycler_view.layoutManager.onSaveInstanceState())
and then restore it
recycler_view.layoutManager.onRestoreInstanceState(it.getParcelable("layout_manager_state"))
failed miserably. Any suggestions are welcome :)
Finally managed to get it working.
Precondition - your PagedListAdapter
must support null placeholders! setEnablePlaceholders(true)
. Read more here
val dataSourceFactory = dao.reportsDataSourceFactory()
val builder = RxPagedListBuilder(
dataSourceFactory,
PagedList.Config.Builder()
.setEnablePlaceholders(true) //in my original implementation it was false
.setInitialLoadSizeHint(60)
.setPageSize(20)
.setPrefetchDistance(60)
.build()
)
Save state as usual:
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
val lastKey = adapter.currentList?.lastKey as Int
outState.putInt("lastKey", lastKey)
outState.putParcelable("layout_manager_state", recycler_view.layoutManager.onSaveInstanceState())
}
but when restoring - first save state as variable and only restore saved state after submitting list to the PagedListAdapter
private fun showReports(pagedList: PagedList<Report>?) {
adapter.submitList(pagedList)
lastLayoutManagerState?.let {
report_list.layoutManager.onRestoreInstanceState(lastLayoutManagerState)
lastLayoutManagerState = null
}
}
where lastLayoutManagerState
is:
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
viewModel = withViewModel(viewModelFactory) {
observe(reports, ::showReports)
}
report_list.adapter = adapter
lastLayoutManagerState = savedInstanceState?.getParcelable("layout_manager_state")
val lastKey = savedInstanceState?.getInt("lastKey")
viewModel.getReports(lastKey)
}
Oh and when binding ViewHolder
in onBindViewHolder
I just bail out fast if item is null.
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val item = getItem(position) ?: return
...
}
Because it will be null as otherwise adapter item count won't match with saved state item count (guessing here) and that is why in some of my experiments layout was jumping around on pages 2+ while it worked on page 1.
If there are better ways how to approach this without manually storing and then using lastLayoutManagerState
- let me know.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With