Ladestatus verwalten und einblenden

Die Paging-Bibliothek verfolgt den Status von Ladeanfragen für Seiteninhalte und stellt ihn über die LoadState Klasse zur Verfügung.

Für jedes LoadType und Datenquelltyp (entweder PagingSource oder RemoteMediator) wird ein separates LoadState-Signal bereitgestellt. Das CombinedLoadStates -Objekt, das vom Listener bereitgestellt wird, enthält Informationen zum Ladestatus aus allen diesen Signalen. Anhand dieser detaillierten Informationen können Sie Ihren Nutzern die entsprechenden Ladeindikatoren anzeigen.

Ladestatus

Die Paging-Bibliothek stellt den Ladestatus für die Verwendung in der UI über das LoadState-Objekt zur Verfügung. LoadState -Objekte können je nach aktuellem Ladestatus eine von drei Formen annehmen:

  • Wenn kein aktiver Ladevorgang und kein Fehler vorliegt, ist LoadState ein LoadState.NotLoading Objekt. Diese Unterklasse enthält auch die endOfPaginationReached Property, die angibt, ob das Ende der Paginierung erreicht wurde.
  • Wenn ein aktiver Ladevorgang vorliegt, ist LoadState ein LoadState.Loading Objekt.
  • Wenn ein Fehler vorliegt, ist LoadState ein LoadState.Error-Objekt.

Sie können auf diese Status über die loadState Property Ihres LazyPagingItems Wrappers zugreifen. Sie können diesen Status auf zwei Arten verwenden: Sie können die Sichtbarkeit des Hauptinhalts verwalten (z. B. mit einem Aktualisierungs-Spinner im Vollbildmodus) oder Ladeelemente direkt in Ihren LazyColumn-Stream einfügen (z. B. einen Fußzeilen-Spinner).

Mit einem Listener auf den Ladestatus zugreifen

Wenn Sie den Ladestatus in Ihrer UI beobachten möchten, verwenden Sie die loadState Property , die vom LazyPagingItems Wrapper bereitgestellt wird. Dadurch wird ein CombinedLoadStates-Objekt zurückgegeben, mit dem Sie auf das Ladeverhalten für Aktualisierungs-, Anfüge- oder Voranfügeereignisse reagieren können.

Im folgenden Beispiel zeigt die UI je nach aktuellem Status des Aktualisierungsladevorgangs (initial) einen Lade-Spinner oder eine Fehlermeldung an:

@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 Datensätze (Paginierung).

Wenn Sie am Anfang oder Ende Ihrer Liste Ladeindikatoren als Kopf- oder Fußzeilen anzeigen möchten, fügen Sie innerhalb des Bereichs LazyColumn spezielle Elementblöcke für diese Status hinzu.

Mit dem Objekt CombinedLoadStates können Sie den Voranfügestatus für die Kopfzeile und den Anfügestatus für die Fußzeile beobachten.

Im folgenden Beispiel wird unten in 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 Ladestatus zugreifen

Wie in den vorherigen Beispielen gezeigt, ist der Aufruf von pagingItems.loadState.refresh praktisch. Dadurch wird jedoch der Unterschied zwischen dem Laden aus Ihrer lokalen Datenbank (PagingSource) und Ihrem Netzwerk (RemoteMediator) verschleiert. Das kann dazu führen, dass in der UI kurz ein Lade-Spinner angezeigt wird, obwohl die Daten im Cache sofort verfügbar sind.

Wenn Sie eine genaue Steuerung benötigen, z. B. um einen Lade-Spinner nur dann anzuzeigen, wenn die lokale Datenbank leer ist und eine Netzwerksynchronisierung aktiv ist, greifen Sie direkt in Ihrer zusammensetzbaren Funktion auf die Properties 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()
    }
}

Auf Änderungen des Ladestatus reagieren

Möglicherweise müssen Sie einmalige Nebeneffekte auslösen, die auf Änderungen des Ladestatus basieren, z. B. zum Anfang einer Liste scrollen oder eine Snackbar anzeigen, wenn eine Aktualisierung abgeschlossen ist.

Verwenden Sie snapshotFlow in einem LaunchedEffect, um Statusänderungen als Stream zu beobachten. So können Sie Standardoperatoren für Flow wie filter und distinctUntilChanged 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)
    }
}

Zusätzliche Ressourcen

Weitere Informationen zur Paging-Bibliothek und zu Ladestatus finden Sie in den folgenden Ressourcen.

Dokumentation

Views-Inhalte