Ladestatus verwalten und einblenden

Die Paging-Bibliothek verfolgt den Status von Ladeanforderungen für Seitendaten und stellt mithilfe der Klasse LoadState. Ihre App kann einen Listener mit der PagingDataAdapter bis Informationen zum aktuellen Status abrufen und die Benutzeroberfläche entsprechend aktualisieren. Diese Status werden vom Adapter bereitgestellt, da sie mit der Benutzeroberfläche synchron sind. Das bedeutet, dass Ihr Listener nach Abschluss des Seitenaufbaus Updates erhält. auf die Benutzeroberfläche angewendet.

Für jedes Feld wird ein separates LoadState-Signal bereitgestellt. LoadType und Datenquellentyp (entweder PagingSource oder RemoteMediator. Die CombinedLoadStates. -Objekt, das vom Listener bereitgestellt wird, liefert Informationen zum Ladestatus aus all diesen Signalen. Anhand dieser detaillierten Informationen können Sie die angemessene Ladeindikatoren für Ihre Nutzer.

Status werden geladen

Die Paging-Bibliothek stellt den Ladestatus zur Verwendung in der Benutzeroberfläche über die LoadState-Objekt. LoadState-Objekte können eine von drei Formen annehmen, je nachdem, Aktueller Ladestatus:

  • Wenn kein aktiver Ladevorgang und kein Fehler vorhanden ist, ist LoadState ein LoadState.NotLoading -Objekt enthält. Diese abgeleitete Klasse enthält auch den endOfPaginationReached , der angibt, ob das Ende der Paginierung erreicht wurde.
  • Wenn es einen aktiven Ladevorgang gibt, ist LoadState ein LoadState.Loading -Objekt enthält.
  • Falls ein Fehler vorliegt, ist LoadState ein Objekt LoadState.Error.

Es gibt zwei Möglichkeiten, LoadState in Ihrer UI zu verwenden: mithilfe eines Listeners oder mithilfe eines Listenadapter verwenden, um den Ladestatus direkt im RecyclerView Liste.

Mit einem Listener auf den Ladestatus zugreifen

Um den Ladestatus für den allgemeinen Gebrauch in Ihrer Benutzeroberfläche abzurufen, verwenden Sie die Methode loadStateFlow oder die addLoadStateListener() von PagingDataAdapter bereitgestellt. Diese Mechanismen ermöglichen den Zugriff auf Ein CombinedLoadStates-Objekt, das Informationen zum LoadState enthält für jeden Ladetyp.

Im folgenden Beispiel zeigt PagingDataAdapter eine andere UI an Komponenten abhängig vom aktuellen Status des Aktualisierungsladevorgangs:

Kotlin

// Activities can use lifecycleScope directly, but Fragments should instead use
// viewLifecycleOwner.lifecycleScope.
lifecycleScope.launch {
  pagingAdapter.loadStateFlow.collectLatest { loadStates ->
    progressBar.isVisible = loadStates.refresh is LoadState.Loading
    retry.isVisible = loadState.refresh !is LoadState.Loading
    errorMsg.isVisible = loadState.refresh is LoadState.Error
  }
}

Java

pagingAdapter.addLoadStateListener(loadStates -> {
  progressBar.setVisibility(loadStates.refresh instanceof LoadState.Loading
    ? View.VISIBLE : View.GONE);
  retry.setVisibility(loadStates.refresh instanceof LoadState.Loading
    ? View.GONE : View.VISIBLE);
  errorMsg.setVisibility(loadStates.refresh instanceof LoadState.Error
    ? View.VISIBLE : View.GONE);
});

Java

pagingAdapter.addLoadStateListener(loadStates -> {
  progressBar.setVisibility(loadStates.refresh instanceof LoadState.Loading
    ? View.VISIBLE : View.GONE);
  retry.setVisibility(loadStates.refresh instanceof LoadState.Loading
    ? View.GONE : View.VISIBLE);
  errorMsg.setVisibility(loadStates.refresh instanceof LoadState.Error
    ? View.VISIBLE : View.GONE);
});

Weitere Informationen zu CombinedLoadStates finden Sie unter Auf zusätzliche Ladevorgänge zugreifen Informationen zum Bundesstaat.

Ladestatus mit einem Adapter darstellen

Die Paging-Bibliothek bietet einen weiteren Listenadapter namens LoadStateAdapter für den wird der Ladezustand direkt in der angezeigten Liste der Seiten Daten. Dieser Adapter bietet Zugriff auf den aktuellen Ladestatus der Liste, der die Sie an einen benutzerdefinierten Ansichts-Inhaber übergeben können, der die Informationen anzeigt.

Erstellen Sie zunächst eine Ansichtshalteklasse, die Verweise auf die Lade- und Fehler Ansichten auf Ihrem Bildschirm. Erstellen Sie eine bind()-Funktion, die LoadState als . Diese Funktion sollte die Ansicht der Ansicht je nach Auslastung ein- oder ausschalten state-Parameter an:

Kotlin

class LoadStateViewHolder(
  parent: ViewGroup,
  retry: () -> Unit
) : RecyclerView.ViewHolder(
  LayoutInflater.from(parent.context)
    .inflate(R.layout.load_state_item, parent, false)
) {
  private val binding = LoadStateItemBinding.bind(itemView)
  private val progressBar: ProgressBar = binding.progressBar
  private val errorMsg: TextView = binding.errorMsg
  private val retry: Button = binding.retryButton
    .also {
      it.setOnClickListener { retry() }
    }

  fun bind(loadState: LoadState) {
    if (loadState is LoadState.Error) {
      errorMsg.text = loadState.error.localizedMessage
    }

    progressBar.isVisible = loadState is LoadState.Loading
    retry.isVisible = loadState is LoadState.Error
    errorMsg.isVisible = loadState is LoadState.Error
  }
}

Java

class LoadStateViewHolder extends RecyclerView.ViewHolder {
  private ProgressBar mProgressBar;
  private TextView mErrorMsg;
  private Button mRetry;

  LoadStateViewHolder(
    @NonNull ViewGroup parent,
    @NonNull View.OnClickListener retryCallback) {
    super(LayoutInflater.from(parent.getContext())
      .inflate(R.layout.load_state_item, parent, false));

    LoadStateItemBinding binding = LoadStateItemBinding.bind(itemView);
    mProgressBar = binding.progressBar;
    mErrorMsg = binding.errorMsg;
    mRetry = binding.retryButton;
  }

  public void bind(LoadState loadState) {
    if (loadState instanceof LoadState.Error) {
      LoadState.Error loadStateError = (LoadState.Error) loadState;
      mErrorMsg.setText(loadStateError.getError().getLocalizedMessage());
    }
    mProgressBar.setVisibility(loadState instanceof LoadState.Loading
      ? View.VISIBLE : View.GONE);
    mRetry.setVisibility(loadState instanceof LoadState.Error
      ? View.VISIBLE : View.GONE);
    mErrorMsg.setVisibility(loadState instanceof LoadState.Error
      ? View.VISIBLE : View.GONE);
  }
}

Java

class LoadStateViewHolder extends RecyclerView.ViewHolder {
  private ProgressBar mProgressBar;
  private TextView mErrorMsg;
  private Button mRetry;

  LoadStateViewHolder(
    @NonNull ViewGroup parent,
    @NonNull View.OnClickListener retryCallback) {
    super(LayoutInflater.from(parent.getContext())
      .inflate(R.layout.load_state_item, parent, false));

    LoadStateItemBinding binding = LoadStateItemBinding.bind(itemView);
    mProgressBar = binding.progressBar;
    mErrorMsg = binding.errorMsg;
    mRetry = binding.retryButton;
  }

  public void bind(LoadState loadState) {
    if (loadState instanceof LoadState.Error) {
      LoadState.Error loadStateError = (LoadState.Error) loadState;
      mErrorMsg.setText(loadStateError.getError().getLocalizedMessage());
    }
    mProgressBar.setVisibility(loadState instanceof LoadState.Loading
      ? View.VISIBLE : View.GONE);
    mRetry.setVisibility(loadState instanceof LoadState.Error
      ? View.VISIBLE : View.GONE);
    mErrorMsg.setVisibility(loadState instanceof LoadState.Error
      ? View.VISIBLE : View.GONE);
  }
}

Erstellen Sie als Nächstes eine Klasse, die LoadStateAdapter implementiert, und definieren Sie die onCreateViewHolder() und onBindViewHolder() . Mit diesen Methoden wird eine Instanz des Halters der benutzerdefinierten Ansicht erstellt und gebunden. zugehöriger Ladestatus.

Kotlin

// Adapter that displays a loading spinner when
// state is LoadState.Loading, and an error message and retry
// button when state is LoadState.Error.
class ExampleLoadStateAdapter(
  private val retry: () -> Unit
) : LoadStateAdapter<LoadStateViewHolder>() {

  override fun onCreateViewHolder(
    parent: ViewGroup,
    loadState: LoadState
  ) = LoadStateViewHolder(parent, retry)

  override fun onBindViewHolder(
    holder: LoadStateViewHolder,
    loadState: LoadState
  ) = holder.bind(loadState)
}

Java

// Adapter that displays a loading spinner when
// state is LoadState.Loading, and an error message and retry
// button when state is LoadState.Error.
class ExampleLoadStateAdapter extends LoadStateAdapter<LoadStateViewHolder> {
  private View.OnClickListener mRetryCallback;

  ExampleLoadStateAdapter(View.OnClickListener retryCallback) {
    mRetryCallback = retryCallback;
  }

  @NotNull
  @Override
  public LoadStateViewHolder onCreateViewHolder(@NotNull ViewGroup parent,
    @NotNull LoadState loadState) {
    return new LoadStateViewHolder(parent, mRetryCallback);
  }

  @Override
  public void onBindViewHolder(@NotNull LoadStateViewHolder holder,
    @NotNull LoadState loadState) {
    holder.bind(loadState);
  }
}

Java

// Adapter that displays a loading spinner when
// state is LoadState.Loading, and an error message and retry
// button when state is LoadState.Error.
class ExampleLoadStateAdapter extends LoadStateAdapter<LoadStateViewHolder> {
  private View.OnClickListener mRetryCallback;

  ExampleLoadStateAdapter(View.OnClickListener retryCallback) {
    mRetryCallback = retryCallback;
  }

  @NotNull
  @Override
  public LoadStateViewHolder onCreateViewHolder(@NotNull ViewGroup parent,
    @NotNull LoadState loadState) {
    return new LoadStateViewHolder(parent, mRetryCallback);
  }

  @Override
  public void onBindViewHolder(@NotNull LoadStateViewHolder holder,
    @NotNull LoadState loadState) {
    holder.bind(loadState);
  }
}

Um den Ladefortschritt in einer Kopf- und Fußzeile anzuzeigen, rufen Sie die Methode withLoadStateHeaderAndFooter() aus dem PagingDataAdapter-Objekt:

Kotlin

pagingAdapter
  .withLoadStateHeaderAndFooter(
    header = ExampleLoadStateAdapter(adapter::retry),
    footer = ExampleLoadStateAdapter(adapter::retry)
  )

Java

pagingAdapter
  .withLoadStateHeaderAndFooter(
    new ExampleLoadStateAdapter(pagingAdapter::retry),
    new ExampleLoadStateAdapter(pagingAdapter::retry));

Java

pagingAdapter
  .withLoadStateHeaderAndFooter(
    new ExampleLoadStateAdapter(pagingAdapter::retry),
    new ExampleLoadStateAdapter(pagingAdapter::retry));

Sie können stattdessen folgende Nummer anrufen: withLoadStateHeader() oder withLoadStateFooter() wenn Sie möchten, dass die RecyclerView-Liste den Ladestatus nur im oder nur in der Fußzeile.

Auf zusätzliche Informationen zum Ladestatus zugreifen

Das CombinedLoadStates-Objekt von PagingDataAdapter liefert Informationen zu die Ladestatus für Ihre PagingSource-Implementierung und auch für Ihre RemoteMediator-Implementierung, falls vorhanden.

Der Einfachheit halber können Sie refresh, append und prepend Eigenschaften von CombinedLoadStates auf ein LoadState-Objekt für die passenden Ladetyp aus. Diese Properties rufen in der Regel den Ladestatus vom die RemoteMediator-Implementierung, falls vorhanden; Andernfalls enthalten sie den Parameter entsprechenden Ladestatus aus der PagingSource-Implementierung. Detailliertere Informationen Informationen zur zugrunde liegenden Logik finden Sie in der Referenzdokumentation zu CombinedLoadStates

Kotlin

lifecycleScope.launch {
  pagingAdapter.loadStateFlow.collectLatest { loadStates ->
    // Observe refresh load state from RemoteMediator if present, or
    // from PagingSource otherwise.
    refreshLoadState: LoadState = loadStates.refresh
    // Observe prepend load state from RemoteMediator if present, or
    // from PagingSource otherwise.
    prependLoadState: LoadState = loadStates.prepend
    // Observe append load state from RemoteMediator if present, or
    // from PagingSource otherwise.
    appendLoadState: LoadState = loadStates.append
  }
}

Java

pagingAdapter.addLoadStateListener(loadStates -> {
  // Observe refresh load state from RemoteMediator if present, or
  // from PagingSource otherwise.
  LoadState refreshLoadState = loadStates.refresh;
  // Observe prepend load state from RemoteMediator if present, or
  // from PagingSource otherwise.
  LoadState prependLoadState = loadStates.prepend;
  // Observe append load state from RemoteMediator if present, or
  // from PagingSource otherwise.
  LoadState appendLoadState = loadStates.append;
});

Java

pagingAdapter.addLoadStateListener(loadStates -> {
  // Observe refresh load state from RemoteMediator if present, or
  // from PagingSource otherwise.
  LoadState refreshLoadState = loadStates.refresh;
  // Observe prepend load state from RemoteMediator if present, or
  // from PagingSource otherwise.
  LoadState prependLoadState = loadStates.prepend;
  // Observe append load state from RemoteMediator if present, or
  // from PagingSource otherwise.
  LoadState appendLoadState = loadStates.append;
});

Beachten Sie jedoch, dass nur die Ladestatus PagingSource synchron mit UI-Updates. Da die refresh, Die Properties append und prepend können möglicherweise den Ladestatus von entweder PagingSource oder RemoteMediator, werden diese synchron mit UI-Updates. Dies kann zu Problemen mit der Benutzeroberfläche führen. bis die neuen Daten der Benutzeroberfläche hinzugefügt wurden.

Aus diesem Grund eignen sich die Convenience-Zugriffsfunktionen gut für die Anzeige der Last in einer Kopf- oder Fußzeile sein. Für andere Anwendungsfälle müssen Sie auf den Ladestatus entweder über PagingSource oder RemoteMediator. CombinedLoadStates stellt die source und mediator für diesen Zweck verwenden. Diese Eigenschaften stellen jeweils ein LoadStates-Objekt, das enthält die LoadState-Objekte für PagingSource oder RemoteMediator Entsprechend:

Kotlin

lifecycleScope.launch {
  pagingAdapter.loadStateFlow.collectLatest { loadStates ->
    // Directly access the RemoteMediator refresh load state.
    mediatorRefreshLoadState: LoadState? = loadStates.mediator.refresh
    // Directly access the RemoteMediator append load state.
    mediatorAppendLoadState: LoadState? = loadStates.mediator.append
    // Directly access the RemoteMediator prepend load state.
    mediatorPrependLoadState: LoadState? = loadStates.mediator.prepend
    // Directly access the PagingSource refresh load state.
    sourceRefreshLoadState: LoadState = loadStates.source.refresh
    // Directly access the PagingSource append load state.
    sourceAppendLoadState: LoadState = loadStates.source.append
    // Directly access the PagingSource prepend load state.
    sourcePrependLoadState: LoadState = loadStates.source.prepend
  }
}

Java

pagingAdapter.addLoadStateListener(loadStates -> {
  // Directly access the RemoteMediator refresh load state.
  LoadState mediatorRefreshLoadState = loadStates.mediator.refresh;
  // Directly access the RemoteMediator append load state.
  LoadState mediatorAppendLoadState = loadStates.mediator.append;
  // Directly access the RemoteMediator prepend load state.
  LoadState mediatorPrependLoadState = loadStates.mediator.prepend;
  // Directly access the PagingSource refresh load state.
  LoadState sourceRefreshLoadState = loadStates.source.refresh;
  // Directly access the PagingSource append load state.
  LoadState sourceAppendLoadState = loadStates.source.append;
  // Directly access the PagingSource prepend load state.
  LoadState sourcePrependLoadState = loadStates.source.prepend;
});

Java

pagingAdapter.addLoadStateListener(loadStates -> {
  // Directly access the RemoteMediator refresh load state.
  LoadState mediatorRefreshLoadState = loadStates.mediator.refresh;
  // Directly access the RemoteMediator append load state.
  LoadState mediatorAppendLoadState = loadStates.mediator.append;
  // Directly access the RemoteMediator prepend load state.
  LoadState mediatorPrependLoadState = loadStates.mediator.prepend;
  // Directly access the PagingSource refresh load state.
  LoadState sourceRefreshLoadState = loadStates.source.refresh;
  // Directly access the PagingSource append load state.
  LoadState sourceAppendLoadState = loadStates.source.append;
  // Directly access the PagingSource prepend load state.
  LoadState sourcePrependLoadState = loadStates.source.prepend;
});

Kettenoperatoren bei LoadState

Da das CombinedLoadStates-Objekt Zugriff auf alle Änderungen in ist es wichtig, den Laststatusstream auf der Grundlage bestimmter Ereignisse. Dadurch wird sichergestellt, dass Sie Ihre UI zum richtigen Zeitpunkt aktualisieren, um und unnötige Aktualisierungen der Benutzeroberfläche.

Angenommen, Sie möchten eine leere Ansicht anzeigen, aber erst nach dem das anfängliche Laden der Daten abgeschlossen ist. Für diesen Anwendungsfall müssen Sie überprüfen, Aktualisierungsladevorgang gestartet wurde. Warten Sie dann, bis der Status NotLoading bestätigt, dass die Aktualisierung abgeschlossen ist. Sie müssen alle Signale bis auf die herausfiltern. Sie benötigen:

Kotlin

lifecycleScope.launchWhenCreated {
  adapter.loadStateFlow
    // Only emit when REFRESH LoadState for RemoteMediator changes.
    .distinctUntilChangedBy { it.refresh }
    // Only react to cases where REFRESH completes, such as NotLoading.
    .filter { it.refresh is LoadState.NotLoading }
    // Scroll to top is synchronous with UI updates, even if remote load was
    // triggered.
    .collect { binding.list.scrollToPosition(0) }
}

Java

PublishSubject<CombinedLoadStates> subject = PublishSubject.create();
Disposable disposable =
  subject.distinctUntilChanged(CombinedLoadStates::getRefresh)
  .filter(
    combinedLoadStates -> combinedLoadStates.getRefresh() instanceof LoadState.NotLoading)
  .subscribe(combinedLoadStates -> binding.list.scrollToPosition(0));

pagingAdapter.addLoadStateListener(loadStates -> {
  subject.onNext(loadStates);
});

Java

LiveData<CombinedLoadStates> liveData = new MutableLiveData<>();
LiveData<LoadState> refreshLiveData =
  Transformations.map(liveData, CombinedLoadStates::getRefresh);
LiveData<LoadState> distinctLiveData =
  Transformations.distinctUntilChanged(refreshLiveData);

distinctLiveData.observeForever(loadState -> {
  if (loadState instanceof LoadState.NotLoading) {
    binding.list.scrollToPosition(0);
  }
});

In diesem Beispiel wird gewartet, bis der Ladestatus der Aktualisierung aktualisiert wurde. Es wird aber nur ausgelöst, wenn der Status NotLoading ist. Dadurch wird sichergestellt, dass bei der Remote-Aktualisierung bevor Änderungen an der Benutzeroberfläche erfolgen.

Stream-APIs machen diese Art von Vorgang möglich. Ihre Anwendung kann die Last angeben, und verarbeiten die neuen Daten, wenn die entsprechenden Kriterien erfüllt sind.