Calling refresh method on paging data in jetpack compose not working correctly
I resolve this issues by returning null in the getRefreshKey()
method
How can I correctly use custom PagingSource with PagingDataAdapter, on local data?
UPDATE July 2022
There are some problems with paging-3. The most noticeable ones are
- failure to properly load the dataset when using an initial position with
initialKey
(see initial_key branch) - failure to properly jump to another position with
scrollToPositionWithOffset
or similar function (see test_scroll branch)
getRefreshKey
function is called with weird (sometimes negative) values of anchorPosition
, and load is called multiple times to load unnecessary data - sometimes loading iteratively every page from the end or from the beginning.
I believe the same problem can be observed when the PagingSource is generated by Room.
=> In short, it can only be used if you need to display the top items first, and if you don't need to jump to a particular position (seeing other items is only done when the user manually scrolls through the list). A fix is on the way but it could take time; it's still only alpha and compiles for a specific set of library versions / SDK.
You'll find another, simpler algorithm for getRefreshKey
and load
in the mentioned branches, in CheeseDao.kt.
End of update
I finally found out a possible solution, though I'm not sure it will work if the Paging-3 library is updated. I don't know yet if the behaviour explained above is due to bugs / limitations in the Paging-3 components or if that's just a bad explanation in the reference documentation, or something I missed entirely.
I. Work-around for the glitch issue.
In
PagingConfig
, we must haveenablePlaceholders = true
or it simply won't work correctly. Withfalse
I'm observing the scrollbar jumping on eachload
operation when scrolling up/down, and inserting items at the end will make all items glitch on the display, then the list will jump all the way to the top.The logic in
getRefreshKey
andload
, as shown in Google's guide and reference documentation, is naive and will not work with custom data. I had to modify them as follows (modifications have been pushed in the github example):
override fun getRefreshKey(state: PagingState<Int, Cheese>): Int? = state.anchorPosition
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Cheese> {
data class Args(
var start: Int = 0, var size: Int = 0, var prevKey: Int? = null, var nextKey: Int? = null,
var itemsBefore: Int = UNDEF, var itemsAfter: Int = UNDEF
)
val pos = params.key ?: 0
val args = Args()
when (params) {
is LoadParams.Append -> {
args.start = pos
args.prevKey = params.key
//args.nextKey = if (args.start < count) min(args.start + params.loadSize, count) else null
args.nextKey = if (args.start + params.loadSize < count) args.start + params.loadSize else null
}
is LoadParams.Prepend -> {
args.start = max(pos - pageSize, 0)
args.prevKey = if (args.start > 0) args.start else null
args.nextKey = params.key
}
is LoadParams.Refresh -> {
args.start = max((pos - params.loadSize/2)/pageSize*pageSize, 0)
args.prevKey = if (args.start > 0) args.start else null
args.nextKey = if (args.start + params.loadSize < count) min(args.start + params.loadSize, count - 1) else null
}
}
args.size = min(params.loadSize, count - args.start)
if (params is LoadParams.Refresh) {
args.itemsBefore = args.start
args.itemsAfter = count - args.size - args.start
}
val source = dao.allCheesesOrdName()
val data = source.drop(args.start).take(args.size)
if (params.key == null && data.count() == 0) {
return LoadResult.Error(Exception("Empty"))
}
val result = LoadResult.Page(
data = data,
prevKey = args.prevKey,
nextKey = args.nextKey,
itemsBefore = args.itemsBefore,
itemsAfter = args.itemsAfter
)
return result
}
I had to deduce that from the behaviour of Room-generated PagingSource, by inserting a wrapper between the DAO code and the view model to observe the params
and LoadResult
values.
Notes
- The commented line in
LoadParam.Append
mimics the Room-generated code behaviour, which is slightly incorrect when loading the last page. It makes the Pager loads an empty page at the end, which is not a serious issue but triggers unnecessary operations in the whole chain. - I'm not sure about the
Refresh
case, it was hard to induce any logic from Room's code behaviour. Here I'm placing theparams.key
position in the middle of the loaded range (params.loadSize
items). At worst it will append data in a 2nd load operation.
II. Predicting the discarded data
This should be possible from the params
given to the load
function. In a simplified heuristic (the ranges must be min/max'ed to get actual indices):
LoadParams.Append -> loads [key .. key+loadSize[, so discard [key-maxSize..key-maxSize+loadSize[
LoadParams.Prepend -> loads [key-loadSize .. key[, do discard [key-loadSize+maxSize..key+maxSize[
LoadParams.Refresh -> discard what is not reloaded
The Args.start .. Args.start + Args.size
in the code above can be used as the range which is kept, from there it is easy to deduce what is discarded.
Could not get unknown property 'implementation' for object of type Dependency
I have added it like this and it works:
implementation 'androidx.paging:paging-runtime-ktx:3.1.0'
Related Topics
Unsupportedoperationexception: Can't Convert to Dimension: Type=0X1
How to Put Multiple Project_Number/Sender Id in Google-Services.JSON
The Completest Cocos2D-X Tutorial & Guide List
How to Put a Border Around an Android Textview
How to Create a Listview with Rounded Corners in Android
How to Set Different Label for Launcher Rather Than Activity Title
How to Implement Drawerarrowtoggle from Android Appcompat V7 21 Library
This Version of the Application Is Not Configured for Billing Through Google Play
Android Preventing Double Click on a Button
Android Linearlayout:Add Border with Shadow Around a Linearlayout
How to Increase Storage for Android Emulator? (Install_Failed_Insufficient_Storage)
Android Singletask or Singleinstance Launch Mode
How to Access Call Log for Android
How to Prevent an Android Device from Going to Sleep Programmatically
Android. How Does Notifydatasetchanged() Method and Listviews Work