Reputation: 187
I have implemented Paging 3 with RemoteMediator. And also have a LoadStateAdapter.
Initially, I was experiencing flickers, glitches and jumps when scrolling pages. This answer worked to resolve the issue - https://stackoverflow.com/a/66713643/15392387
I can see 3 items in my RecyclerView in one screen, so setting the PageSize = 8, as suggested, resolved all flickering issues.
But since I also use PagingDataAdapter.withLoadStateHeaderAndFooter, the initial load when the app is installed, automatically scrolls down to the 8th ListItem.
It does not start from the top of the page.
Can someone help me resolve this issue?
I found an answer that might be talking about the same issue, but solution is still unclear - https://stackoverflow.com/a/66763460/15392387
HomeFragment.kt
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
_binding = FragmentHomeBinding.bind(view) //View Binding
val parentAdapter = PlaylistParentPagingAdapter(this, this)
binding.apply {
playlistParentRecyclerView.setHasFixedSize(true)
playlistParentRecyclerView.adapter = parentAdapter.withLoadStateHeaderAndFooter(
header = PlaylistLoadStateAdapter { parentAdapter.retry() },
footer = PlaylistLoadStateAdapter { parentAdapter.retry() },
)
}
viewModel.playlists.observe(viewLifecycleOwner) {
parentAdapter.submitData(viewLifecycleOwner.lifecycle, it)
}
}
PlaylistLoadStateAdapter.kt
class PlaylistLoadStateAdapter(private val retry: () -> Unit) :
LoadStateAdapter<PlaylistLoadStateAdapter.LoadStateViewHolder>() {
private val TAG = "PlaylistLoadStateAdapte"
override fun onCreateViewHolder(parent: ViewGroup, loadState: LoadState): LoadStateViewHolder {
val binding = PlaylistLoadStateFooterBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
return LoadStateViewHolder(binding)
}
override fun onBindViewHolder(holder: LoadStateViewHolder, loadState: LoadState) {
holder.bind(loadState)
}
inner class LoadStateViewHolder(private val binding: PlaylistLoadStateFooterBinding) :
RecyclerView.ViewHolder(binding.root) {
init {
binding.retryButtonFooter.setOnClickListener {
retry.invoke()
}
}
fun bind(loadState: LoadState) {
binding.apply {
Log.d(TAG, "bind: loadstate = $loadState")
progressBarFooter.isVisible = loadState is LoadState.Loading
retryButtonFooter.isVisible = loadState !is LoadState.Loading
errorTextViewFooter.isVisible = loadState !is LoadState.Loading
}
}
}
Upvotes: 0
Views: 1199
Reputation: 3895
There is an open feature request for RecyclerView where we need a way to ignore the header / footers when considering restoring scroll position. Essentially you have a list which only contains the footer, then a list which contains some items + the footer, and RV is trying to restore the scroll position to the footer since that is the only overlap.
You can follow the issue here: https://issuetracker.google.com/issues/184874613
One workaround is to use placeholders, but if that's unsuitable you could also try a custom LoadStateAdapter
that is only visible after PagingSource
is done loading locally. This will cause your footer to get remove and re-added during local refresh, but it most likely will not be visible to the user.
Upvotes: 1