Android Compose Paging 3 - 通过网络调用一次性加载所有页面,无需在 LazyColumn 中滚动且无内部滚动

Kar*_*eek 2 android android-mvvm android-paging android-jetpack-compose android-paging-3

我正在开发一个 Android Compose 项目,其中使用 Paging 3 和 LazyColumn 来显示从网络调用获取的项目列表。Paging 3 的默认行为是根据prefetchDistance用户滚动来加载页面,但在我的例子中,它会加载大约 15 个页面,每个页面在屏幕启动时包含 10 条记录。我尝试过将其设置prefetchDistace为 10,但不起作用。

这是我的代码: ViewModel

class ShowAllNotificationsViewModel @Inject constructor(private val pagingSource: NotificationPagingSource) :
    BaseViewModel() {


    private val _uiState =
        MutableStateFlow<UIState<BaseResponseModel<NotificationResponseModel?>>>(UIState.StateLoading)
    val uiState = _uiState.asStateFlow()

    private val _isRefreshing = MutableStateFlow(false)
    val isRefreshing = _isRefreshing.asStateFlow()


    val items: Flow<PagingData<NotificationModel>> = Pager(
        config = PagingConfig(
            pageSize = 10,
            prefetchDistance = 5,
            enablePlaceholders = false,
        ),
        pagingSourceFactory = { pagingSource }
    )
        .flow
        .cachedIn(viewModelScope)

    fun refreshData() {
        pagingSource.invalidate()
    }
}
Run Code Online (Sandbox Code Playgroud)

分页来源:

class NotificationPagingSource @Inject constructor(val requestNotificationsUseCase: GetNotificationsUseCase) :
    PagingSource<String, NotificationModel>() {

    override suspend fun load(params: LoadParams<String>): LoadResult<String, NotificationModel> {
        Timber.e(IllegalStateException())
        Timber.d("load params = $params")
        val responseModel = requestNotificationsUseCase(params.key)
        val result = responseModel?.getAsResult();
        result?.let {
            return@load when (result) {
                is Result.Error<*>,
                Result.Loading,
                is Result.SessionTimeoutError<*> -> {
                    LoadResult.Error(
                        SessionTimeoutException()
                    )
                }

                is Result.Success -> {

                    LoadResult.Page(
                        result.value.data?.notifications ?: listOf(),
                        prevKey = null,
                        nextKey = result.value.data?.lastId,
                    )
                }
            }
        }
        return LoadResult.Error(
            IllegalStateException()
        )
    }

    override fun getRefreshKey(state: PagingState<String, NotificationModel>): String? {
        Timber.d("getRefreshKey = state = $state")
        return null
    }
}
Run Code Online (Sandbox Code Playgroud)

用户界面:

 @Composable
    fun ShowAllNotificationCompose(items: LazyPagingItems<NotificationModel>) {
        Column(
            modifier = Modifier
                .fillMaxSize()
                .background(Color.Black)
        ) {
            Timber.d("ShowAllNotificationCompose : state = ${items.loadState}")
            HeaderCompose()

            Box(
                modifier = Modifier
                    .weight(1f)
            ) {
                val listState = rememberLazyListState()

                LazyColumn(
                    modifier = Modifier,
                    state = listState
                ) {
                    items(
                        count = items.itemCount,
                        key = {
                            items[it]?.id ?: 0
                        }
                    ) {
                        val notificationModel = items[it]
                        if (notificationModel != null) {
                            NotificationItem(item = notificationModel)
                        } else {
                            NotificationShimmerItem()
                        }
                    }
                   if (items.loadState.append == LoadState.Loading) {
                        items(2) {
                            NotificationShimmerItem()
                        }
                    }
                }
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

有没有办法修改此设置以确保仅在到达prefectchDistance. 提前致谢。

Jan*_*ína 5

问题出在你的key

key = { items[it]?.id ?: 0 }
Run Code Online (Sandbox Code Playgroud)

当您调用LazyPagingItems::get它时,它会通知 Paging 项目访问,这可以触发页面加载。来自文档:

返回指定位置处呈现的项目,通知 Paging 项目访问以触发满足 prefetchDistance 所需的任何加载。

并且key立即为所有项目调用 ,而不仅仅是在显示它们时。所以对于 key,你应该使用该peek函数。或者您可以使用itemKey,它在内部使用 peek 。

key = { items.peek(it)?.id ?: 0 }

key = items.itemKey { it.id }
Run Code Online (Sandbox Code Playgroud)