Die Paging-Bibliothek verfolgt den Status von Ladeanfragen für paginierte Daten und stellt ihn über die Klasse LoadState bereit.
Für jeden LoadType und Datenquellentyp (entweder PagingSource oder RemoteMediator) wird ein separates LoadState-Signal bereitgestellt. Das vom Listener bereitgestellte CombinedLoadStates-Objekt enthält Informationen zum Ladestatus aller dieser Signale. Anhand dieser detaillierten Informationen können Sie Ihren Nutzern die entsprechenden Ladeanzeigen präsentieren.
Bundesstaaten werden geladen
Die Paging-Bibliothek stellt den Ladestatus für die Verwendung in der Benutzeroberfläche über das LoadState-Objekt bereit. LoadState-Objekte können je nach aktuellem Ladestatus eine von drei Formen annehmen:
- Wenn kein aktiver Ladevorgang und kein Fehler vorliegt, ist
LoadStateeinLoadState.NotLoading-Objekt. Diese Unterklasse enthält auch das AttributendOfPaginationReached, das angibt, ob das Ende der Paginierung erreicht wurde. - Wenn ein aktiver Ladevorgang vorhanden ist, ist
LoadStateeinLoadState.Loading-Objekt. - Wenn ein Fehler auftritt, ist
LoadStateeinLoadState.Error-Objekt.
Auf diese Status können Sie über das Attribut loadState des LazyPagingItems-Wrappers zugreifen. Du kannst diesen Status auf zwei Arten verwenden: Du kannst die Sichtbarkeit des Hauptinhalts steuern (z. B. mit einem Vollbild-Ladesymbol) oder Ladeelemente direkt in deinen LazyColumn-Stream einfügen (z. B. mit einem Ladesymbol in der Fußzeile).
Mit einem Listener auf den Ladestatus zugreifen
Wenn Sie den Ladestatus in Ihrer Benutzeroberfläche überwachen möchten, verwenden Sie das Attribut loadState, das vom Wrapper LazyPagingItems bereitgestellt wird. Dadurch wird ein CombinedLoadStates-Objekt zurückgegeben, mit dem Sie auf das Ladeverhalten für Aktualisierungs-, Anfüge- oder Voranstellungsereignisse reagieren können.
Im folgenden Beispiel wird in der Benutzeroberfläche je nach aktuellem Status des Aktualisierungsvorgangs (anfänglicher Ladevorgang) ein rotierendes Ladesymbol oder eine Fehlermeldung angezeigt:
@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 } } }
Weitere Informationen zu LazyPagingItems finden Sie unter Große Datasets (Paging).
Kopf- und Fußzeilen für das Laden hinzufügen
Wenn Sie Ladeanzeigen am Anfang oder Ende Ihrer Liste (als Kopf- oder Fußzeilen) anzeigen möchten, fügen Sie im LazyColumn-Bereich spezielle Elementblöcke für diese Status hinzu.
Mit dem Objekt CombinedLoadStates können Sie den Status des Voranstellens für den Header und den Status des Anfügens für den Footer überwachen.
Im folgenden Beispiel wird am Ende der Liste eine Fortschrittsanzeige oder eine Schaltfläche zum Wiederholen angezeigt, wenn weitere Daten abgerufen werden:
@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") } } }
Auf zusätzliche Informationen zum Ladezustand zugreifen
Wie in den vorherigen Beispielen gezeigt, ist der Aufruf von pagingItems.loadState.refresh praktisch. Der Unterschied zwischen dem Laden aus der lokalen Datenbank (PagingSource) und dem Laden aus dem Netzwerk (RemoteMediator) wird jedoch verschleiert. Dies kann dazu führen, dass auf der Benutzeroberfläche kurz ein Ladesymbol angezeigt wird, obwohl im Cache gespeicherte Daten sofort verfügbar sind.
Wenn Sie eine präzise Steuerung wünschen, z. B. um ein Ladesymbol nur dann anzuzeigen, wenn die lokale Datenbank leer ist und eine Netzwerksynchronisierung aktiv ist, greifen Sie direkt in Ihrer zusammensetzbaren Funktion auf die Attribute source und mediator zu.
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() } }
Da pagingItems.loadState jedes Mal ein neues CombinedLoadStates-Objekt ausgibt, wenn sich prepend oder append beim Scrollen ändert, verpacken Sie die Aktualisierungsprüfung in derivedStateOf mit remember(pagingItems). Dadurch wird verhindert, dass Aktualisierungen am Scrollrand einen übergeordneten Bereich neu zusammensetzen, der nur eine Aktualisierungsanzeige im Vollbildmodus umschaltet:
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() } }
Auf Änderungen des Ladestatus reagieren
Möglicherweise müssen Sie einmalige Nebenwirkungen basierend auf Änderungen des Ladestatus auslösen, z. B. zum Anfang einer Liste scrollen oder eine Snackbar anzeigen, wenn eine Aktualisierung abgeschlossen ist oder fehlschlägt.
Verwenden Sie snapshotFlow in einem LaunchedEffect, um Statusänderungen als Stream zu beobachten. So können Sie Standard-Flow-Operatoren wie filter, distinctUntilChanged und distinctUntilChangedBy anwenden, um bestimmte Ereignisse zu isolieren.
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) } }
Ebenso können Sie distinctUntilChangedBy für loadState verwenden, um den Fehler Snackbar nur dann anzuzeigen, wenn der Status refresh zu LoadState.Error wechselt:
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() } } }
Zusätzliche Ressourcen
Weitere Informationen zur Paging-Bibliothek und zu Ladestatus finden Sie in den folgenden Ressourcen.
Dokumentation
Inhalte ansehen
Empfehlungen für Sie
- Hinweis: Der Linktext wird angezeigt, wenn JavaScript deaktiviert ist.
- Paging-Bibliothek – Übersicht