Biblioteka Paging śledzi stan żądań wczytywania danych podzielonych na strony i udostępnia go za pomocą klasy LoadState.
Dla każdego LoadType i typu źródła danych (PagingSource lub RemoteMediator) jest dostarczany osobny sygnał LoadState. Obiekt CombinedLoadStates dostarczany przez detektor zawiera informacje o stanie wczytywania ze wszystkich tych sygnałów. Możesz użyć tych szczegółowych informacji, aby wyświetlać użytkownikom odpowiednie wskaźniki wczytywania.
Wczytuję stany
Biblioteka Paging udostępnia stan wczytywania do użycia w interfejsie za pomocą obiektu LoadState. Obiekty LoadState mogą przyjmować jedną z 3 form w zależności od bieżącego stanu wczytywania:
- Jeśli nie ma aktywnej operacji wczytywania i nie wystąpił błąd,
LoadStatejest obiektemLoadState.NotLoading. Ta podklasa zawiera też właściwośćendOfPaginationReached, która wskazuje, czy osiągnięto koniec paginacji. - Jeśli trwa operacja wczytywania,
LoadStatejest obiektemLoadState.Loading. - Jeśli wystąpi błąd,
LoadStatejest obiektemLoadState.Error.
Dostęp do tych stanów możesz uzyskać za pomocą właściwości loadState obiektu opakowującego LazyPagingItems. Możesz użyć tego stanu na 2 sposoby: obsługując widoczność treści głównej (np. wskaźnik odświeżania na pełnym ekranie) lub wstawiając elementy wczytywania bezpośrednio do strumienia LazyColumn (np. wskaźnik w stopce).
Uzyskiwanie dostępu do stanu wczytywania za pomocą detektora
Aby monitorować stan wczytywania w interfejsie, użyj właściwości loadState udostępnianej przez kod LazyPagingItems. Zwraca obiekt CombinedLoadStates, który pozwala reagować na zachowanie podczas wczytywania w przypadku zdarzeń odświeżania, dołączania lub dołączania na początku.
W poniższym przykładzie interfejs wyświetla wskaźnik postępu wczytywania lub komunikat o błędzie w zależności od bieżącego stanu odświeżania (początkowego) wczytywania:
@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 } } }
Więcej informacji o funkcji LazyPagingItems znajdziesz w artykule Duże zbiory danych (stronicowanie).
Dodawanie nagłówków i stopek wczytywania
Aby wyświetlać wskaźniki wczytywania na początku lub na końcu listy (jako nagłówki lub stopki), dodaj dedykowane bloki elementów dla tych stanów w zakresie LazyColumn.
Stan dołączenia nagłówka i stopki możesz monitorować za pomocą obiektu CombinedLoadStates.
W tym przykładzie lista wyświetla pasek postępu lub przycisk ponownej próby u dołu, gdy pobierane są dodatkowe dane:
@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") } } }
Dostęp do dodatkowych informacji o stanie wczytywania
Jak widać na wcześniejszych przykładach, wywoływanie funkcji pagingItems.loadState.refresh jest wygodne. Zaciera jednak różnicę między wczytywaniem z lokalnej bazy danych (PagingSource) a wczytywaniem z sieci (RemoteMediator).
Może to spowodować, że w interfejsie na chwilę pojawi się wskaźnik wczytywania, nawet jeśli dane z pamięci podręcznej są natychmiast dostępne.
Aby uzyskać precyzyjną kontrolę, np. wyświetlać wskaźnik ładowania tylko wtedy, gdy lokalna baza danych jest pusta, a synchronizacja sieci jest aktywna, uzyskaj dostęp do właściwości source i mediator bezpośrednio w funkcji kompozycyjnej.
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() } }
pagingItems.loadState emituje nowy obiekt CombinedLoadStates za każdym razem, gdy prepend lub append zmienia się podczas przewijania, dlatego opakuj sprawdzanie odświeżania w derivedStateOf za pomocą remember(pagingItems). Zapobiega to ponownemu tworzeniu zakresu nadrzędnego przez aktualizacje krawędzi przewijania, które przełączają tylko wskaźnik odświeżania pełnego ekranu:
val showFullScreenLoading by remember(pagingItems) { derivedStateOf { // Avoid recomposing on prepend or append updates while scrolling, and // avoid covering existing items during a background refresh. pagingItems.loadState.source.refresh is LoadState.Loading && pagingItems.itemCount == 0 } } if (showFullScreenLoading) { Box( modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center ) { CircularProgressIndicator() } }
Reagowanie na zmiany stanu wczytywania
Może być konieczne wywołanie jednorazowych efektów ubocznych na podstawie zmian stanu wczytywania, takich jak przewijanie do góry listy lub wyświetlanie Snackbar po zakończeniu lub niepowodzeniu odświeżania.
Użyj snapshotFlow wewnątrz LaunchedEffect, aby obserwować zmiany stanu jako strumień. Dzięki temu możesz stosować standardowe operatory Flow, takie jak filter, distinctUntilChanged i distinctUntilChangedBy, aby wyodrębniać konkretne zdarzenia.
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) } }
Podobnie możesz użyć distinctUntilChangedBy w loadState, aby wyświetlić błąd Snackbar tylko wtedy, gdy stan refresh zmieni się na LoadState.Error:
val snackbarHostState = remember { SnackbarHostState() } // Key on pagingItems rather than loadState so state updates do not restart the // coroutine and reset distinctUntilChangedBy. LaunchedEffect(pagingItems) { snapshotFlow { pagingItems.loadState } // Ignore prepend and append updates so scrolling does not re-trigger the // refresh error Snackbar. .distinctUntilChangedBy { it.refresh } .filter { it.refresh is LoadState.Error } .collect { loadState -> val error = (loadState.refresh as LoadState.Error).error val result = snackbarHostState.showSnackbar( message = "Data failed to load: ${error.localizedMessage ?: "Unknown error"}", actionLabel = "Retry" ) if (result == SnackbarResult.ActionPerformed) { pagingItems.retry() } } }
Dodatkowe materiały
Więcej informacji o bibliotece Paging i stanach wczytywania znajdziesz w tych materiałach.
Dokumentacja
Wyświetla treści
Polecane dla Ciebie
- Uwaga: tekst linku jest wyświetlany, gdy język JavaScript jest wyłączony.
- Omówienie biblioteki Paging