gpt4 book ai didi

Android Paging 3 库 PagingSource 失效,刷新键错误导致列表跳转(不使用房间)

转载 作者:行者123 更新时间:2023-12-05 00:00:49 33 4
gpt4 key购买 nike

由于我目前正在使用自定义数据库(而不是 Room)进行项目,因此我正在测试我们是否可以在项目中使用 Paging 3 库。
但是,我遇到了一个问题,如果您更改数据并因此使分页源无效,则列表的重新创建是错误的并跳转到不同的位置。发生这种情况是因为 Refresh Key 计算似乎是错误的,这很可能是由于初始加载加载了三页数据,但将其放入一页。
默认分页源如下所示:

    override fun getRefreshKey(state: PagingState<Int, CustomData>): Int? {
// Try to find the page key of the closest page to anchorPosition, from
// either the prevKey or the nextKey, but you need to handle nullability
// here:
// * prevKey == null -> anchorPage is the first page.
// * nextKey == null -> anchorPage is the last page.
// * both prevKey and nextKey null -> anchorPage is the initial page, so
// just return null.
return state.anchorPosition?.let { anchorPosition ->
val anchorPage = state.closestPageToPosition(anchorPosition)
anchorPage?.prevKey?.plus(1) ?: anchorPage?.nextKey?.minus(1)
}
}

override suspend fun load(params: LoadParams<Int>): LoadResult<Int, CustomData> {
var pagePosition = params.key ?: STARTING_PAGE_INDEX
var loadSize = params.loadSize
return try {
val dataResult = dataRepository.getPagedData(
pagePosition = pagePosition,
loadSize = loadSize,
pageSize = pageSize
)
val nextKey = if (dataResult.isEmpty() || dataResult.size < pageSize) {
null
} else {
pagePosition + (loadSize / pageSize)
}
Log.i(
"RoomFreePagingSource",
"page $pagePosition with size $loadSize publish ${dataResult.size} routes"
)
return LoadResult.Page(
data = dataResult,
prevKey = if (pagePosition == STARTING_PAGE_INDEX) null else pagePosition - 1,
nextKey = nextKey
)

} catch (exception: Exception) {
LoadResult.Error(exception)
}
}
dataRepository.getPagedData()函数简单地访问内存列表并返回列表的一个子部分,模拟分页数据。
为了完整起见,这里是这个函数的实现:
fun getPagedData(pageSize:Int, loadSize:Int, pagePosition: Int): List<CustomData>{
val startIndex = pagePosition * pageSize
val endIndexExl =startIndex + loadSize
return data.safeSubList(startIndex,endIndexExl).map { it.copy() }
}

private fun <T> List<T>.safeSubList(fromIndex: Int, toIndex: Int) : List<T>{
// only returns list with valid range, to not throw exception
if(fromIndex>= this.size)
return emptyList()
val endIndex = if(toIndex> this.size) this.size else toIndex
return subList(fromIndex,endIndex)
}
我目前面临的主要问题是, getRefreshKey函数没有返回正确的刷新页面键,导致刷新错误的页面,列表跳转到加​​载的页面。
  • 例如,如果您每页有 10 个项目。
  • 第一页 0 包含 30 项
  • 下一页将是 3 并包含 10 个项目。
  • 如果您不滚动(仅查看前 7 个项目)并使其无效,那么 anchorPosition将为 7,刷新键为 2。
    (anchorPage.prevKey = null => anchorPage.nextKey = 3 => 3-1 是 2)
    但是,此时我们要加载第 0 页而不是第 2 页。

  • 知道问题的原因后,我尝试调整默认实现来解决它,并提出了许多不同的版本。下面的一个目前效果最好,但仍然会不时引起跳跃。有时列表的一部分会闪烁,这可能是由于没有足够的项目被提取来填充视口(viewport)。
    override fun getRefreshKey(state: PagingState<Int, CustomData>): Int? {
    return state.anchorPosition?.let { anchorPosition ->
    val closestPage = state.closestPageToPosition(anchorPosition)?.prevKey
    ?: STARTING_PAGE_INDEX
    val refKey = if(anchorPosition>(closestPage)*pageSize + pageSize)
    closestPage+1
    else
    closestPage

    Log.i(
    "RoomFreePagingSource",
    "getRefreshKey $refKey from anchorPosition $anchorPosition closestPage $closestPage"
    )
    refKey
    }
    }

    override suspend fun load(params: LoadParams<Int>): LoadResult<Int, CustomData> {
    var pagePosition = params.key ?: STARTING_PAGE_INDEX
    var loadSize = pageSize
    return try {
    when (params) {
    is LoadParams.Refresh -> {
    if (pagePosition > STARTING_PAGE_INDEX) {
    loadSize *= 3
    } else if (pagePosition == STARTING_PAGE_INDEX) {
    loadSize *= 2
    }
    }
    else -> {}
    }
    val dataResult = dataRepository.getPagedData(
    pagePosition = pagePosition,
    loadSize = loadSize,
    pageSize = pageSize
    )
    val nextKey = if (dataResult.isEmpty() || dataResult.size < pageSize) {
    null
    } else {
    pagePosition + (loadSize / pageSize)
    }
    Log.i(
    "RouteRepository",
    "page $pagePosition with size $loadSize publish ${dataResult.size} routes"
    )
    return LoadResult.Page(
    data = dataResult,
    prevKey = if (pagePosition == STARTING_PAGE_INDEX) null else pagePosition - 1,
    nextKey = nextKey
    )

    } catch (exception: Exception) {
    LoadResult.Error(exception)
    }
    }
    理论上,我发现最好的解决方案是像这样计算刷新 PageKey anchorPosition/pageSize然后加载这个页面,它之前和之后的页面。但是,像这样计算刷新键不起作用,因为 anchorPosition 不是列表中项目的实际位置。在一些失效之后,即使您当前正在查看列表中的第 140 项, anchor 也可能为 5。
    所以总结一下:
    当我不使用 Room 而是使用另一个数据源时,如何在失效后计算正确的刷新页面,例如在这个示例中我通过 getPagedData 访问的内存列表中?

    最佳答案

    同时找到了我自己的解决方案,但忘记在此处添加答案。

  • anchorPositiongetRefreshKey()错了,因为 itemsBefore没有为分页源返回的每个 Page 设置参数。此行为似乎不是 documented .
    LoadResult.Page(
    itemsBefore = pagePosition * pageSize,
    data = dataResult,
    prevKey = if (pagePosition == STARTING_PAGE_INDEX) null else pagePosition - 1,
    nextKey = nextKey
    )
  • 现在可以根据 anchorPosition 实际确定刷新键。和 pageSize (在此示例中定义为 PagingSource 构造函数参数,但也可以是常量等)
    override fun getRefreshKey(state: PagingState<Int, CustomData>): Int? {
    return state.anchorPosition?.let { anchorPosition ->
    anchorPosition / pageSize
    }
    }
  • 不,列表在刷新/无效时不再跳转。但是,有时列表可能会闪烁或列表的整个部分在失效时将不可见。这是因为刷新页面太小,没有覆盖整个视口(viewport)。因此,可以看到刷新页面之外的项目的分页操作。可以通过使刷新页面 (loadSize) 大 3 倍并减小 pagePosition 来修复此行为。一个。需要 3 次,因为实际刷新页面之前和之后的页面可以在视口(viewport)中可见。
    if (params is LoadParams.Refresh) {
    loadSize *= 3
    pagePosition = max(0, pagePosition - 1)
    }

  • 这个解决方案现在工作得很好,但是,感觉这不能/不应该是这个问题的官方解决方案。因为在使用分页和房间时,所有这些调整都不是必需的。所以,我对其他/更好的解决方案持开放态度。
    这里是工作 PagingSource 的完整代码以及所有提到的更改:
    override fun getRefreshKey(state: PagingState<Int, CustomData>): Int? {
    return state.anchorPosition?.let { anchorPosition ->
    anchorPosition / pageSize
    }
    }

    override suspend fun load(params: LoadParams<Int>): LoadResult<Int, CustomData> {
    var pagePosition = params.key ?: STARTING_PAGE_INDEX
    var loadSize = pageSize
    return try {
    if (params is LoadParams.Refresh) {
    // make sure everything visible in the view port is updated / loaded
    loadSize *= 3
    pagePosition = max(0, pagePosition - 1)
    }
    val dataResult = dataRepository.getPagedData(
    pagePosition = pagePosition,
    loadSize = loadSize,
    pageSize = pageSize
    )
    val nextKey = if (dataResult.isEmpty() || dataResult.size < pageSize) {
    null
    } else {
    pagePosition + (loadSize / pageSize)
    }
    LoadResult.Page(
    itemsBefore = pagePosition * pageSize,
    data = dataResult,
    prevKey = if (pagePosition == STARTING_PAGE_INDEX) null else pagePosition - 1,
    nextKey = nextKey
    )
    } catch (exception: Exception) {
    LoadResult.Error(exception)
    }
    }

    关于Android Paging 3 库 PagingSource 失效,刷新键错误导致列表跳转(不使用房间),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70127248/

    33 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com