Die Paging-Bibliothek verfolgt den Status von Ladeanfragen für ausgelagerte Daten und stellt sie über die Klasse LoadState
bereit.
Ihre App kann einen Listener mit dem PagingDataAdapter
registrieren, um Informationen zum aktuellen Status zu erhalten und die UI entsprechend zu aktualisieren. Diese Status werden vom Adapter bereitgestellt, da sie mit der UI synchron sind.
Das bedeutet, dass der Listener Aktualisierungen erhält, wenn der Seitenaufbau auf die UI angewendet wurde.
Für jeden LoadType
- und Datenquellentyp (entweder PagingSource
oder RemoteMediator
) wird ein separates LoadState
-Signal bereitgestellt. Das vom Listener bereitgestellte Objekt CombinedLoadStates
liefert Informationen zum Ladestatus aller dieser Signale. Anhand dieser detaillierten Informationen kannst du deinen Nutzern die entsprechenden Ladeindikatoren anzeigen.
Status werden geladen
Die Paginierungsbibliothek zeigt den Ladestatus zur Verwendung in der UI über das LoadState
-Objekt an. LoadState
-Objekte nehmen je nach aktuellem Ladestatus eine von drei Formen an:
- Wenn kein aktiver Ladevorgang und kein Fehler vorhanden ist, ist
LoadState
einLoadState.NotLoading
-Objekt. Diese abgeleitete Klasse enthält auch die PropertyendOfPaginationReached
, die angibt, ob das Ende der Paginierung erreicht wurde. - Bei einem aktiven Ladevorgang ist
LoadState
einLoadState.Loading
-Objekt. - Bei einem Fehler ist
LoadState
einLoadState.Error
-Objekt.
Es gibt zwei Möglichkeiten, LoadState
in Ihrer UI zu verwenden: Sie können entweder einen Listener verwenden oder einen speziellen Listenadapter, um den Ladestatus direkt in der Liste RecyclerView
darzustellen.
Mit einem Listener auf den Ladestatus zugreifen
Um den Ladestatus für die allgemeine Verwendung in Ihrer UI abzurufen, verwenden Sie den Stream loadStateFlow
oder die Methode addLoadStateListener()
, die von Ihrer PagingDataAdapter
bereitgestellt wird. Diese Mechanismen bieten Zugriff auf ein CombinedLoadStates
-Objekt, das Informationen zum LoadState
-Verhalten für jeden Ladetyp enthält.
Im folgenden Beispiel werden im PagingDataAdapter
abhängig vom aktuellen Status des Aktualisierungsladevorgangs verschiedene UI-Komponenten angezeigt:
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 Informationen zum Ladestatus zugreifen.
Ladestatus mit einem Adapter darstellen
Die Paging-Bibliothek bietet einen weiteren Listenadapter mit dem Namen LoadStateAdapter
, mit dem der Ladestatus direkt in der angezeigten Liste der ausgelagerten Daten dargestellt wird. Dieser Adapter bietet Zugriff auf den aktuellen Ladestatus der Liste, den Sie an einen Halter einer benutzerdefinierten Ansicht übergeben können, der die Informationen anzeigt.
Erstellen Sie zuerst eine Ansichtsinhaberklasse, die Verweise auf die Lade- und Fehleransichten auf Ihrem Bildschirm beibehält. Erstellen Sie eine bind()
-Funktion, die einen LoadState
als Parameter akzeptiert. Diese Funktion sollte die Sichtbarkeit der Ansicht basierend auf dem Parameter für den Ladestatus ein- und ausschalten:
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 Methoden onCreateViewHolder()
und onBindViewHolder()
. Diese Methoden erstellen eine Instanz Ihres benutzerdefinierten Ansichtsinhabers und binden den zugehörigen 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); } }
Ladestatus als Kopf- oder Fußzeile anzeigen
Rufen Sie die Methode withLoadStateHeaderAndFooter()
über das PagingDataAdapter
-Objekt auf, um den Ladefortschritt in einer Kopf- und Fußzeile anzuzeigen:
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 withLoadStateHeader()
oder withLoadStateFooter()
aufrufen, wenn die RecyclerView
-Liste den Ladestatus nur in der Kopfzeile oder nur in der Fußzeile anzeigen soll.
Auf zusätzliche Informationen zum Ladestatus zugreifen
Das CombinedLoadStates
-Objekt aus PagingDataAdapter
bietet Informationen zu den Ladestatus für Ihre PagingSource
-Implementierung und auch für Ihre RemoteMediator
-Implementierung, falls vorhanden.
Sie können die Attribute refresh
, append
und prepend
von CombinedLoadStates
verwenden, um auf ein LoadState
-Objekt für den entsprechenden Ladetyp zuzugreifen. Diese Attribute richten sich in der Regel auf den Ladestatus der Implementierung RemoteMediator
, sofern vorhanden. Andernfalls enthalten sie den entsprechenden Ladestatus aus der Implementierung PagingSource
. Ausführliche 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
garantiert synchron mit UI-Aktualisierungen sind. Da die Attribute refresh
, append
und prepend
möglicherweise den Ladestatus von PagingSource
oder RemoteMediator
übernehmen können, ist nicht gewährleistet, dass sie mit UI-Aktualisierungen synchron sind. Dies kann zu UI-Problemen führen, wenn der Ladevorgang abgeschlossen ist, bevor der UI neue Daten hinzugefügt wurden.
Aus diesem Grund funktionieren die praktischen Zugriffsfunktionen gut, um den Ladestatus in einer Kopf- oder Fußzeile anzuzeigen. In anderen Fällen müssen Sie jedoch möglicherweise über PagingSource
oder RemoteMediator
speziell auf den Ladestatus zugreifen. CombinedLoadStates
stellt die Attribute source
und mediator
für diesen Zweck bereit. Mit diesen Attributen wird jeweils ein LoadStates
-Objekt bereitgestellt, das die LoadState
-Objekte für PagingSource
bzw. RemoteMediator
enthält:
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 des Ladestatus bietet, ist es wichtig, den Ladestatusstream anhand bestimmter Ereignisse zu filtern. So wird sichergestellt, dass Sie die UI zum richtigen Zeitpunkt aktualisieren, um ruckelige UI-Aktualisierungen und unnötige Aktualisierungen zu vermeiden.
Angenommen, Sie möchten eine leere Ansicht anzeigen, aber erst nach Abschluss des anfänglichen Ladens der Daten. In diesem Anwendungsfall müssen Sie prüfen, ob der Ladevorgang der Datenaktualisierung gestartet wurde, und dann auf den Status NotLoading
warten, um zu bestätigen, dass die Aktualisierung abgeschlossen ist. Sie müssen alle Signale herausfiltern, mit Ausnahme der, die 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, wird jedoch nur ausgelöst, wenn der Status NotLoading
ist. Dadurch wird sichergestellt, dass die Remote-Aktualisierung vollständig abgeschlossen ist, bevor UI-Aktualisierungen durchgeführt werden.
Stream-APIs ermöglichen diese Art von Vorgang. Ihre Anwendung kann die benötigten Ladeereignisse angeben und die neuen Daten verarbeiten, wenn die entsprechenden Kriterien erfüllt sind.
Empfehlungen für dich
- Hinweis: Der Linktext wird angezeigt, wenn JavaScript deaktiviert ist.
- Auslagerungsdaten laden und anzeigen
- Seite aus Netzwerk und Datenbank
- Paging-Bibliothek – Übersicht