首页 > 解决方案 > 带有 RemoteMediator 的 Android Paging3:项目出现在列表的中间或可见的上方,看起来很乱

问题描述

我有一个来自 API 的项目列表。我正在使用带有 RemoteMedatior 的Paging3来使用 Room 在数据库中使用本地缓存来维护网络。我的问题是,当数据加载时,recyclerView看起来凌乱而有问题,项目出现在列表中间和可见项目之上,并且recyclerView不会滚动到列表顶部。看起来像这样我已经尝试了 pageConfig 的pageSizemaxSizeprefetchDistance
的 各种值,但它没有帮助。 recylerview上的 setHasFixedSize 也不起作用。使用片段中的collectLatest收集流。我也尝试过正常收集并将流量更改为liveData。结果相同。知道如何纠正这个问题吗?

RemoteMediator 中的加载函数

@ExperimentalPagingApi
class RecipesMediator(
    private val recipesApi: RecipesApi,
    private val appDatabase: RecipesDatabase,
    private val queries: HashMap<String, String>,
) : RemoteMediator<Int, Recipe>() {
    override suspend fun load(loadType: LoadType, state: PagingState<Int, Recipe>): MediatorResult {

    val queryId = queries[QUERY_DIET] + queries[QUERY_MEAL]

    val page = when (loadType) {
        LoadType.REFRESH -> 0
        LoadType.PREPEND -> return MediatorResult.Success(endOfPaginationReached = true)
        LoadType.APPEND -> appDatabase.remoteKeysDao().remoteKeysRecipeId(queryId).nextKey
    }
    
    queries[QUERY_OFFSET] = page.toString()
    queries[QUERY_NUMBER] = "20"

    try {
        val response = recipesApi.getRecipes(queries)

        appDatabase.withTransaction {
            if (loadType == LoadType.REFRESH) {
                appDatabase.remoteKeysDao().clearRemoteKeys()
                appDatabase.recipesDao().clearRecipes()
            }

            val nextKey = page + 20
            val keys = response.recipes.map {

                RemoteKeys(query = queryId, nextKey = nextKey)
            }
            appDatabase.remoteKeysDao().insertAll(keys)
            appDatabase.recipesDao().insertAllRecipes(response.recipes)
        }
        return MediatorResult.Success(endOfPaginationReached = response.recipes.isEmpty())
    } catch (exception: IOException) {
        return MediatorResult.Error(exception)
    } catch (exception: HttpException) {
        return MediatorResult.Error(exception)
    }
}

我使用字符串作为远程密钥 ID,因为当我使用 API = LoadType.Append中的项目 ID 时,被多次调用并且只加载了第一页。它在这个问题中解释

nextPage是“page + 20”,因为这个 API(Spoonacular)不提供分页,虽然提供了参数 offset(要跳过的结果数)和 number(预期结果数),所以我将它们用于分页。

片段中的 UI 状态

 recipesAdapter.addLoadStateListener { loadState ->
        binding.apply {
            recipesProgressBar.isVisible = loadState.mediator?.refresh is LoadState.Loading
            recipesProgressBar.isVisible = loadState.source.refresh is LoadState.Loading

            recipesRecyclerView.isVisible =loadState.mediator?.refresh is LoadState.NotLoading
            recipesRecyclerView.isVisible =loadState.source.refresh is LoadState.NotLoading

            buttonRecipesRetry.isVisible =loadState.mediator?.refresh is LoadState.Error
            buttonRecipesRetry.isVisible =loadState.source.refresh is LoadState.Error

            textViewRecipesError.isVisible =loadState.mediator?.refresh is LoadState.Error
            textViewRecipesError.isVisible =loadState.source.refresh is LoadState.Error

            if (loadState.source.refresh is LoadState.NotLoading &&
                loadState.mediator?.refresh is LoadState.NotLoading &&
                loadState.append.endOfPaginationReached &&
                recipesAdapter.itemCount < 1
            ) {
                recipesRecyclerView.isVisible = false
                textViewEmpty.isVisible = true
            } else {
                textViewEmpty.isVisible = false
            }
        }
    }

遥控钥匙

@Entity(tableName = "remoteKeys")
data class RemoteKeys
(@PrimaryKey val query: String, val nextKey: Int)

遥控钥匙道

@Dao
interface RemoteKeysDao {

@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertAll(remoteKey: List<RemoteKeys>)

@Query("SELECT * FROM remoteKeys WHERE `query` = :query")
suspend fun remoteKeysId(query: String): RemoteKeys

@Query("DELETE FROM remoteKeys")
suspend fun clearRemoteKeys()
}

标签: androidandroid-recyclerviewandroid-paging-3

解决方案


推荐阅读