La libreria Paging offre potenti funzionalità per il caricamento e la visualizzazione di dati paginati da un set di dati più grande. Questa guida mostra come utilizzare la libreria Paging per configurare un flusso di dati impaginati da un'origine dati di rete e visualizzarli in un elenco pigro.
Definire un'origine dati
Il primo passaggio consiste nel definire un'implementazione PagingSource per identificare l'origine dati. La classe API PagingSource include il metodo load,
che esegui l'override per indicare come recuperare i dati impaginati dall'origine dati
corrispondente.
Utilizza direttamente la classe PagingSource per utilizzare le coroutine Kotlin per il caricamento asincrono.
Seleziona i tipi di chiave e valore
PagingSource<Key, Value> ha due parametri di tipo: Key e Value. La chiave
definisce l'identificatore utilizzato per caricare i dati, mentre il valore è il tipo di
dati stesso. Ad esempio, se carichi pagine di oggetti User dalla rete
passando i numeri di pagina Int a Retrofit, seleziona Int come tipo Key
e User come tipo Value.
Definisci PagingSource
L'esempio seguente implementa un PagingSource che carica le pagine degli elementi
in base al numero di pagina. Il tipo Key è Int e il tipo Value è User.
class ExamplePagingSource(
val backend: ExampleBackendService,
val query: String
) : PagingSource<Int, User>() {
override suspend fun load(
params: LoadParams<Int>
): LoadResult<Int, User> {
init {
// the data source is expected to be immutable
// invalidate PagingSource if data source
// has updated
backEnd.addDatabaseOnChangedListener {
invalidate()
}
}
try {
// Start refresh at page 1 if undefined.
val nextPageNumber = params.key ?: 1
val response = backend.searchUsers(query, nextPageNumber)
return LoadResult.Page(
data = response.users,
prevKey = null, // Only paging forward.
nextKey = nextPageNumber + 1
)
} catch (e: Exception) {
// Handle errors in this block and return LoadResult.Error for
// expected errors (such as a network failure).
}
}
override fun getRefreshKey(state: PagingState<Int, User>): Int? {
// Try to find the page key of the closest page to anchorPosition from
// either the prevKey or the nextKey; you need to handle nullability
// here.
// * prevKey == null -> anchorPage is the first page.
// * nextKey == null -> anchorPage is the last page.
// * both prevKey and nextKey are null -> anchorPage is the
// initial page, so return null.
return state.anchorPosition?.let { anchorPosition ->
val anchorPage = state.closestPageToPosition(anchorPosition)
anchorPage?.prevKey?.plus(1) ?: anchorPage?.nextKey?.minus(1)
}
}
}
Una tipica implementazione di PagingSource passa i parametri forniti nel suo costruttore al metodo load per caricare i dati appropriati per una query. Nell'esempio
riportato sopra, questi parametri sono:
backend: un'istanza del servizio di backend che fornisce i datiquery: la query di ricerca da inviare al servizio indicato dabackend
L'oggetto LoadParams
contiene informazioni sull'operazione di caricamento da eseguire. Sono inclusi la chiave da caricare e il numero di elementi da caricare.
L'oggetto LoadResult
contiene il risultato dell'operazione di caricamento. LoadResult è una classe sigillata
che assume una delle tre forme, a seconda che la chiamata load sia andata a buon fine:
- Se il caricamento ha esito positivo, restituisci un oggetto
LoadResult.Page. - Se il caricamento non va a buon fine, restituisci un oggetto
LoadResult.Error. - Se
PagingSourcenon è più valido e deve essere sostituito da una nuova istanza (ad esempio, a causa di una modifica dei dati sottostanti), restituisci un oggettoLoadResult.Invalid.
La figura seguente illustra come la funzione load in questo esempio riceve la chiave per ogni caricamento e fornisce la chiave per il caricamento successivo.
load utilizza e aggiorna la chiave.
L'implementazione di PagingSource deve implementare anche un metodo
getRefreshKey che accetta un oggetto
PagingState come parametro. Restituisce la chiave da passare al metodo load quando i dati vengono
aggiornati o invalidati dopo il caricamento iniziale. La libreria Paging chiama questo metodo automaticamente nei successivi aggiornamenti dei dati.
Gestisci gli errori
Le richieste di caricamento dei dati possono non riuscire per diversi motivi, soprattutto quando il caricamento
avviene tramite una rete. Segnala gli errori riscontrati durante il caricamento restituendo un oggetto
LoadResult.Error dal metodo load.
Ad esempio, puoi rilevare e segnalare gli errori di caricamento in ExamplePagingSource
dell'esempio precedente aggiungendo quanto segue al metodo load:
catch (e: IOException) {
// IOException for network failures.
return LoadResult.Error(e)
} catch (e: HttpException) {
// HttpException for any non-2xx HTTP status codes.
return LoadResult.Error(e)
}
Per ulteriori informazioni sulla gestione degli errori di Retrofit, consulta gli esempi nel
riferimento API PagingSource.
PagingSource raccoglie e distribuisce gli oggetti LoadResult.Error all'interfaccia utente in modo che tu possa agire di conseguenza. Per saperne di più sull'esposizione dello stato di caricamento nell'interfaccia utente, consulta Gestire e presentare gli stati di caricamento.
Configurare un flusso di PagingData
A questo punto, hai bisogno di un flusso di dati paginati dall'implementazione di PagingSource.
Configura lo stream di dati in ViewModel. La classe Pager fornisce metodi che espongono un flusso reattivo di oggetti PagingData da un PagingSource. La libreria Paging espone il flusso di dati come Flow.
Quando crei un'istanza Pager per configurare lo stream reattivo, devi
fornire all'istanza un oggetto di configurazione PagingConfig e una
funzione che indichi a Pager come ottenere un'istanza dell'implementazione di PagingSource, come mostrato nell'esempio seguente.
class UserViewModel(
private val backend: ExampleBackendService,
private val query: String
) : ViewModel() {
val userPagingFlow: Flow<PagingData<User>> = Pager(
// Configure how data is loaded by passing additional properties to
// PagingConfig, such as pageSize and enabling or disabling placeholders.
config = PagingConfig(
pageSize = 20,
enablePlaceholders = true
),
pagingSourceFactory = {
ExamplePagingSource(backend, query)
}
)
.flow
.cachedIn(viewModelScope)
}
L'operatore cachedIn rende condivisibile lo stream di dati e memorizza nella cache i dati caricati con il CoroutineScope fornito. Senza cachedIn, non è possibile
recuperare PagingData. Questo esempio utilizza viewModelScope fornito dall'artefatto
del ciclo di vita lifecycle-viewmodel-ktx.
L'oggetto Pager chiama il metodo load dall'oggetto PagingSource,
fornendogli l'oggetto LoadParams e ricevendo in cambio l'oggetto
LoadResult.
Raccogliere e visualizzare i dati nell'interfaccia utente
Per connettere lo stream paginato all'UI, ottieni il flusso da ViewModel e
passalo al composable della lista.
@Composable
fun UserScreen(viewModel: UserViewModel = viewModel()) {
val userFlow = viewModel.userPagingFlow
UserList(flow = userFlow)
}
Utilizza collectAsLazyPagingItems per convertire il flusso PagingData in
LazyPagingItems. Quindi, utilizza l'API items all'interno di un LazyColumn per disporre
ogni elemento.
Assicurati di fornire un identificatore univoco e stabile per ogni articolo utilizzando itemKey.
Il seguente esempio utilizza it.id (che fa riferimento alla proprietà User.id) perché
rimane stabile per l'istanza User durante gli aggiornamenti dei dati.
@Composable
fun UserList(flow: Flow<PagingData<User>>) {
val lazyPagingItems = flow.collectAsLazyPagingItems()
LazyColumn {
items(
lazyPagingItems.itemCount,
key = lazyPagingItems.itemKey { it.id }
) { index ->
val user = lazyPagingItems[index]
if (user != null) {
UserRow(user)
} else {
UserPlaceholder()
}
}
}
}
La libreria Paging utilizza null per i segnaposto durante il caricamento di una pagina, quindi se
hai attivato i segnaposto, devi gestire i valori null nel blocco
di contenuti.
Ora l'elenco mostra i dati paginati e la libreria Paging carica pagine aggiuntive man mano che l'utente scorre la pagina.
Risorse aggiuntive
Per saperne di più sulla libreria Paging, consulta le seguenti risorse aggiuntive:
Documentazione
Visualizza contenuti
Consigliati per te
- Nota: il testo del link viene visualizzato quando JavaScript è disattivato
- Pagina della rete e del database
- Migrazione a Paging 3
- Panoramica della libreria Paging