Библиотека Paging отслеживает состояние запросов на загрузку постраничных данных и предоставляет к ним доступ через класс LoadState .
Для каждого LoadType и типа источника данных ( PagingSource или RemoteMediator ) предоставляется отдельный сигнал LoadState . Объект CombinedLoadStates , предоставляемый слушателем, содержит информацию о состоянии загрузки из всех этих сигналов. Вы можете использовать эту подробную информацию для отображения соответствующих индикаторов загрузки вашим пользователям.
Состояния загрузки
Библиотека Paging предоставляет доступ к состоянию загрузки для использования в пользовательском интерфейсе через объект LoadState . Объекты LoadState принимают одну из трех форм в зависимости от текущего состояния загрузки:
- Если активной операции загрузки нет и ошибок нет, то
LoadStateпредставляет собой объектLoadState.NotLoading. Этот подкласс также включает свойствоendOfPaginationReached, которое указывает, достигнут ли конец пагинации. - Если выполняется активная операция загрузки, то
LoadStateпредставляет собой объектLoadState.Loading. - В случае ошибки,
LoadStateбудет иметь вид объектаLoadState.Error.
Доступ к этим состояниям осуществляется через свойство loadState вашего обертки LazyPagingItems . Вы можете использовать это состояние двумя способами: для управления видимостью основного контента (например, как индикатор обновления на весь экран) или для вставки элементов загрузки непосредственно в поток LazyColumn (например, индикатор загрузки в нижнем колонтитуле).
Доступ к состоянию загрузки осуществляется с помощью слушателя.
Для отслеживания состояния загрузки в пользовательском интерфейсе используйте свойство loadState предоставляемое оберткой LazyPagingItems . Оно возвращает объект CombinedLoadStates , который позволяет реагировать на события загрузки: обновление, добавление или добавление элементов.
В следующем примере пользовательский интерфейс отображает индикатор загрузки или сообщение об ошибке в зависимости от текущего состояния обновления (начальной) загрузки:
@Composable fun UserListScreen(viewModel: UserViewModel) { val pagingItems = viewModel.flow.collectAsLazyPagingItems() Box(modifier = Modifier.fillMaxSize()) { // Show the list content LazyColumn { items(pagingItems.itemCount) { index -> UserItem(pagingItems[index]) } } // Handle the loading state when (val state = pagingItems.loadState.refresh) { is LoadState.Loading -> { CircularProgressIndicator(modifier = Modifier.align(Alignment.Center)) } is LoadState.Error -> { ErrorButton( message = state.error.message ?: "Unknown error", onClick = { pagingItems.retry() }, modifier = Modifier.align(Alignment.Center) ) } else -> {} // No separate view needed for success/not loading } } }
Для получения дополнительной информации о LazyPagingItems см. раздел «Большие наборы данных (постраничная навигация)» .
Добавьте заголовки и нижние колонтитулы загрузки.
Чтобы отображать индикаторы загрузки в начале или конце списка (в качестве заголовков или нижних колонтитулов), добавьте специальные блоки элементов, предназначенные именно для этих состояний, в рамках области видимости LazyColumn .
Отслеживать состояние добавления заголовка (prepend) и состояние добавления нижнего колонтитула (uppend) можно с помощью объекта CombinedLoadStates .
В следующем примере при загрузке дополнительных данных внизу списка отображается индикатор выполнения или кнопка повторной попытки:
@Composable fun UserList(viewModel: UserViewModel) { val pagingItems = viewModel.pager.flow.collectAsLazyPagingItems() LazyColumn { // 1. Header (Prepend state) // Useful if you support bidirectional paging or jumping to the middle item { val prependState = pagingItems.loadState.prepend if (prependState is LoadState.Loading) { LoadingItem() } else if (prependState is LoadState.Error) { ErrorItem( message = prependState.error.message ?: "Error", onClick = { pagingItems.retry() } ) } } // 2. Main Data items(pagingItems.itemCount) { index -> UserItem(pagingItems[index]) } // 3. Footer (Append state) // Shows when the user scrolls to the bottom and more data is loading item { val appendState = pagingItems.loadState.append if (appendState is LoadState.Loading) { LoadingItem() } else if (appendState is LoadState.Error) { ErrorItem( message = appendState.error.message ?: "Error", onClick = { pagingItems.retry() } ) } } } } @Composable fun LoadingItem() { Box(modifier = Modifier.fillMaxWidth().padding(16.dp), contentAlignment = Alignment.Center) { CircularProgressIndicator() } } @Composable fun ErrorItem(message: String, onClick: () -> Unit) { Column( modifier = Modifier.fillMaxWidth().padding(16.dp), horizontalAlignment = Alignment.CenterHorizontally ) { Text(text = message, color = Color.Red) Button(onClick = onClick) { Text("Retry") } } }
Получите доступ к дополнительной информации о состоянии загрузки.
Как показано в предыдущих примерах, вызов pagingItems.loadState.refresh удобен. Однако он скрывает разницу между загрузкой из локальной базы данных ( PagingSource ) и из сети ( RemoteMediator ). Это может привести к тому, что в пользовательском интерфейсе на короткое время появится индикатор загрузки, даже если кэшированные данные доступны немедленно.
Для точного управления, например, для отображения индикатора загрузки только тогда, когда локальная база данных пуста и активна сетевая синхронизация, обращайтесь к свойствам source и mediator непосредственно в вашем составном объекте.
val loadState = pagingItems.loadState val isSyncing = loadState.mediator?.refresh is LoadState.Loading val isLocalEmpty = loadState.source.refresh is LoadState.NotLoading && pagingItems.itemSnapshotList.items.isEmpty() if (isSyncing && isLocalEmpty) { FullScreenLoading() } else { UserList(pagingItems) if (isSyncing) { TopOverlaySpinner() } }
Реагировать на изменения состояния загрузки
Возможно, вам потребуется запускать разовые побочные эффекты в зависимости от изменений состояния загрузки, например, прокрутку списка вверх или отображение Snackbar после завершения обновления.
Используйте snapshotFlow внутри LaunchedEffect , чтобы отслеживать изменения состояния в виде потока. Это позволяет применять стандартные операторы Flow , такие как filter и distinctUntilChanged для изоляции конкретных событий.
val listState = rememberLazyListState() LaunchedEffect(pagingItems) { // 1. Convert the state to a Flow snapshotFlow { pagingItems.loadState.refresh } // 2. Filter for the specific event (Refresh completed successfully) .distinctUntilChanged() .filter { it is LoadState.NotLoading } .collect { // 3. Trigger the side effect listState.animateScrollToItem(0) } }
Дополнительные ресурсы
Для получения дополнительной информации о библиотеке страничной навигации и состояниях загрузки обратитесь к следующим ресурсам.
Документация
Просмотры контента
{% verbatim %}Рекомендуем вам
- Примечание: текст ссылки отображается, когда JavaScript отключен.
- Обзор библиотеки пейджинга