Molte app devono mostrare raccolte di elementi. Questo documento spiega come eseguire questa operazione in modo efficiente in Jetpack Compose.
Se sai che il tuo caso d'uso non richiede lo scorrimento, ti consigliamo di utilizzare un semplice Column
o Row
(a seconda della direzione) ed emettere i contenuti di ogni elemento iterando su un elenco nel seguente modo:
@Composable fun MessageList(messages: List<Message>) { Column { messages.forEach { message -> MessageRow(message) } } }
Possiamo rendere scorrevole il Column
utilizzando il modificatore verticalScroll()
.
Elenchi lazy
Se devi visualizzare un numero elevato di elementi (o un elenco di lunghezza sconosciuta),
l'utilizzo di un layout come Column
può causare problemi di prestazioni, poiché tutti gli elementi verranno composti e disposti indipendentemente dal fatto che siano visibili o meno.
Compose fornisce un insieme di componenti che compongono e organizzano solo gli elementi visibili nell'area visibile del componente. Questi componenti includono
LazyColumn
e
LazyRow
.
Come suggerisce il nome, la differenza tra
LazyColumn
e
LazyRow
è l'orientamento in cui vengono disposti gli elementi e lo scorrimento. LazyColumn
genera un elenco con scorrimento verticale, mentre LazyRow
genera un elenco con scorrimento orizzontale.
I componenti lazy sono diversi dalla maggior parte dei layout in Compose. Invece di accettare un parametro di blocco dei contenuti @Composable
, che consente alle app di emettere direttamente i composabili, i componenti lazy forniscono un blocco LazyListScope.()
. Questo
blocco LazyListScope
offre un DSL che consente alle app di descrivere i contenuti degli elementi. Il componente Lazy è quindi responsabile dell'aggiunta dei contenuti di ogni elemento in base al layout e alla posizione di scorrimento.
LazyListScope
DSL
Il DSL di LazyListScope
fornisce una serie di funzioni per descrivere gli elementi nel layout. Nella forma più semplice,
item()
aggiunge un singolo elemento, mentre
items(Int)
aggiunge più elementi:
LazyColumn { // Add a single item item { Text(text = "First item") } // Add 5 items items(5) { index -> Text(text = "Item: $index") } // Add another single item item { Text(text = "Last item") } }
Esistono anche una serie di funzioni di estensione che ti consentono di aggiungere raccolte di elementi, ad esempio un List
. Queste estensioni ci consentono di eseguire facilmente la migrazione dell'esempio Column
riportato sopra:
/** * import androidx.compose.foundation.lazy.items */ LazyColumn { items(messages) { message -> MessageRow(message) } }
Esiste anche una variante della funzione di estensione
items()
chiamata
itemsIndexed()
,
che fornisce l'indice. Per ulteriori dettagli, consulta la documentazione di riferimento LazyListScope
.
Griglie lazy
I composabili
LazyVerticalGrid
e
LazyHorizontalGrid
supportano la visualizzazione degli elementi in una griglia. Una griglia verticale lazy visualizza i suoi elementi in un contenitore scorrevole verticalmente, su più colonne, mentre le griglie orizzontali lazy avranno lo stesso comportamento sull'asse orizzontale.
Le griglie hanno le stesse potenti funzionalità dell'API degli elenchi e utilizzano anche un linguaggio DSL molto simile, LazyGridScope.()
, per descrivere i contenuti.
Il parametro columns
in
LazyVerticalGrid
e il parametro rows
in
LazyHorizontalGrid
controllano la modalità di formazione delle celle in colonne o righe. L'esempio riportato di seguito mostra gli elementi in una griglia utilizzando GridCells.Adaptive
per impostare ogni colonna su una larghezza minima di 128.dp
:
LazyVerticalGrid( columns = GridCells.Adaptive(minSize = 128.dp) ) { items(photos) { photo -> PhotoItem(photo) } }
LazyVerticalGrid
ti consente di specificare una larghezza per gli elementi, in modo che la griglia adatti il numero più elevato possibile di colonne. L'eventuale larghezza rimanente viene distribuita equamente tra le colonne dopo il calcolo del numero di colonne.
Questo metodo di determinazione delle dimensioni adattabili è particolarmente utile per visualizzare insiemi di elementi su schermi di dimensioni diverse.
Se conosci il numero esatto di colonne da utilizzare, puoi fornire un'istanza di GridCells.Fixed
contenente il numero di colonne richieste.
Se il tuo design richiede che solo alcuni elementi abbiano dimensioni non standard, puoi utilizzare il supporto della griglia per fornire intervalli di colonne personalizzati per gli elementi.
Specifica l'intervallo di colonne con il parametro span
dei metodi
LazyGridScope DSL
item
e items
.
maxLineSpan
, uno dei valori dell'ambito di span, è particolarmente utile quando utilizzi le dimensioni adattabili, perché il numero di colonne non è fisso.
Questo esempio mostra come specificare un intervallo di righe completo:
LazyVerticalGrid( columns = GridCells.Adaptive(minSize = 30.dp) ) { item(span = { // LazyGridItemSpanScope: // maxLineSpan GridItemSpan(maxLineSpan) }) { CategoryCard("Fruits") } // ... }
Griglia sfalsata lazy
LazyVerticalStaggeredGrid
e
LazyHorizontalStaggeredGrid
sono composabili che ti consentono di creare una griglia sfalsata con caricamento differito di elementi.
Una griglia sfalsata verticale lazy mostra i suoi elementi in un contenitore scorrevole verticalmente che si estende su più colonne e consente ai singoli elementi di avere altezze diverse. Le griglie orizzontali lazy hanno lo stesso comportamento sull'asse orizzontale con elementi di larghezza diversa.
Lo snippet seguente è un esempio di base dell'utilizzo di LazyVerticalStaggeredGrid
con una larghezza 200.dp
per elemento:
LazyVerticalStaggeredGrid( columns = StaggeredGridCells.Adaptive(200.dp), verticalItemSpacing = 4.dp, horizontalArrangement = Arrangement.spacedBy(4.dp), content = { items(randomSizedPhotos) { photo -> AsyncImage( model = photo, contentScale = ContentScale.Crop, contentDescription = null, modifier = Modifier .fillMaxWidth() .wrapContentHeight() ) } }, modifier = Modifier.fillMaxSize() )
Per impostare un numero fisso di colonne, puoi utilizzareStaggeredGridCells.Fixed(columns)
anziché StaggeredGridCells.Adaptive
.
La larghezza disponibile viene divisa per il numero di colonne (o righe per una griglia orizzontale) e ogni elemento occupa questa larghezza (o altezza per una griglia orizzontale):
LazyVerticalStaggeredGrid( columns = StaggeredGridCells.Fixed(3), verticalItemSpacing = 4.dp, horizontalArrangement = Arrangement.spacedBy(4.dp), content = { items(randomSizedPhotos) { photo -> AsyncImage( model = photo, contentScale = ContentScale.Crop, contentDescription = null, modifier = Modifier .fillMaxWidth() .wrapContentHeight() ) } }, modifier = Modifier.fillMaxSize() )
![Griglia sfalsata di immagini con caricamento lento in Scrivi](https://developer.android.google.cn/static/develop/ui/compose/images/lists/staggered_grid_fixed.png?authuser=0&hl=it)
Spaziatura interna dei contenuti
A volte è necessario aggiungere spaziatura ai bordi dei contenuti. I componenti lazy consentono di passare alcuni PaddingValues
al parametro contentPadding
per supportare questa funzionalità:
LazyColumn( contentPadding = PaddingValues(horizontal = 16.dp, vertical = 8.dp), ) { // ... }
In questo esempio, aggiungiamo 16.dp
di spaziatura ai bordi orizzontali (sinistra e
a destra) e 8.dp
in alto e in basso ai contenuti.
Tieni presente che questo spazio viene applicato ai contenuti, non al
LazyColumn
stesso. Nell'esempio precedente, il primo elemento aggiungerà un padding di 8.dp
in alto, l'ultimo aggiungerà 8.dp
in basso e tutti gli elementi
avranno un padding di 16.dp
a sinistra e a destra.
Come altro esempio, puoi passare PaddingValues
di Scaffold
a contentPadding
di LazyColumn
. Consulta la guida su come creare video da un lato all'altro.
Spaziatura dei contenuti
Per aggiungere spazi tra gli elementi, puoi utilizzare
Arrangement.spacedBy()
.
L'esempio seguente aggiunge 4.dp
di spazio tra ogni elemento:
LazyColumn( verticalArrangement = Arrangement.spacedBy(4.dp), ) { // ... }
Analogamente per LazyRow
:
LazyRow( horizontalArrangement = Arrangement.spacedBy(4.dp), ) { // ... }
Le griglie, invece, accettano sia le disposizioni verticali che quelle orizzontali:
LazyVerticalGrid( columns = GridCells.Fixed(2), verticalArrangement = Arrangement.spacedBy(16.dp), horizontalArrangement = Arrangement.spacedBy(16.dp) ) { items(photos) { item -> PhotoItem(item) } }
Chiavi elemento
Per impostazione predefinita, lo stato di ogni elemento è associato alla posizione dell'elemento nell'elenco o nella griglia. Tuttavia, questo può causare problemi se il set di dati cambia, poiché gli elementi che cambiano posizione perdono effettivamente lo stato memorizzato. Se immagini lo scenario di LazyRow
all'interno di un LazyColumn
, se la riga modifica la posizione dell'elemento, l'utente perderà la posizione di scorrimento all'interno della riga.
Per risolvere il problema, puoi fornire una chiave stabile e univoca per ogni elemento, fornendo un blocco al parametro key
. Fornendo una chiave stabile, lo stato dell'elemento sarà coerente nelle modifiche del set di dati:
LazyColumn { items( items = messages, key = { message -> // Return a stable + unique key for the item message.id } ) { message -> MessageRow(message) } }
Fornendo le chiavi, aiuti Compose a gestire correttamente i riordini. Ad esempio, se l'elemento contiene lo stato memorizzato, le chiavi di impostazione consentono a Compose di spostare questo stato insieme all'elemento quando la sua posizione cambia.
LazyColumn { items(books, key = { it.id }) { val rememberedValue = remember { Random.nextInt() } } }
Tuttavia, esiste una limitazione per i tipi che puoi utilizzare come chiavi elemento.
Il tipo di chiave deve essere supportato da Bundle
, il meccanismo di Android per mantenere gli stati quando l'attività viene ricreata. Bundle
supporta tipi come primitivi, enumerazioni o Parcelable.
LazyColumn { items(books, key = { // primitives, enums, Parcelable, etc. }) { // ... } }
La chiave deve essere supportata da Bundle
in modo che il rememberSaveable
all'interno del composable dell'elemento possa essere ripristinato quando l'attività viene ricreata o anche quando scorri da questo elemento e torni indietro.
LazyColumn { items(books, key = { it.id }) { val rememberedValue = rememberSaveable { Random.nextInt() } } }
Animazioni degli elementi
Se hai utilizzato il widget RecyclerView, saprai che anima le modifiche degli elementi automaticamente.
I layout lazy offrono la stessa funzionalità per il riassetto degli elementi.
L'API è semplice: devi solo impostare il modificatore
animateItem
nei contenuti dell'articolo:
LazyColumn { // It is important to provide a key to each item to ensure animateItem() works as expected. items(books, key = { it.id }) { Row(Modifier.animateItem()) { // ... } } }
Se necessario, puoi anche fornire una specifica di animazione personalizzata:
LazyColumn { items(books, key = { it.id }) { Row( Modifier.animateItem( fadeInSpec = tween(durationMillis = 250), fadeOutSpec = tween(durationMillis = 100), placementSpec = spring(stiffness = Spring.StiffnessLow, dampingRatio = Spring.DampingRatioMediumBouncy) ) ) { // ... } } }
Assicurati di fornire chiavi per gli elementi in modo da poter trovare la nuova posizione dell'elemento spostato.
Esempio: animare gli elementi negli elenchi lazy
Con Scrivi, puoi animare le modifiche agli elementi negli elenchi lazy. Se utilizzati insieme, i seguenti snippet implementano le animazioni durante l'aggiunta, la rimozione e la riorganizzazione degli elementi dell'elenco lazy.
Questo snippet mostra un elenco di stringhe con transizioni animate quando gli elementi vengono aggiunti, rimossi o riordinati:
@Composable fun ListAnimatedItems( items: List<String>, modifier: Modifier = Modifier ) { LazyColumn(modifier) { // Use a unique key per item, so that animations work as expected. items(items, key = { it }) { ListItem( headlineContent = { Text(it) }, modifier = Modifier .animateItem( // Optionally add custom animation specs ) .fillParentMaxWidth() .padding(horizontal = 8.dp, vertical = 0.dp), ) } } }
Punti chiave del codice
ListAnimatedItems
mostra un elenco di stringhe in unLazyColumn
con transizioni animate quando gli elementi vengono modificati.- La funzione
items
assegna una chiave univoca a ogni elemento dell'elenco. Compose utilizza i tasti per monitorare gli elementi e identificare le modifiche alle relative posizioni. ListItem
definisce il layout di ogni elemento dell'elenco. Richiede un parametroheadlineContent
, che definisce i contenuti principali dell'elemento.- Il modificatore
animateItem
applica le animazioni predefinite alle aggiunte, alle rimozioni e ai movimenti degli elementi.
Lo snippet seguente mostra una schermata che include controlli per aggiungere e rimuovere elementi, nonché per ordinare un elenco predefinito:
@Composable private fun ListAnimatedItemsExample( data: List<String>, modifier: Modifier = Modifier, onAddItem: () -> Unit = {}, onRemoveItem: () -> Unit = {}, resetOrder: () -> Unit = {}, onSortAlphabetically: () -> Unit = {}, onSortByLength: () -> Unit = {}, ) { val canAddItem = data.size < 10 val canRemoveItem = data.isNotEmpty() Scaffold(modifier) { paddingValues -> Column( modifier = Modifier .padding(paddingValues) .fillMaxSize() ) { // Buttons that change the value of displayedItems. AddRemoveButtons(canAddItem, canRemoveItem, onAddItem, onRemoveItem) OrderButtons(resetOrder, onSortAlphabetically, onSortByLength) // List that displays the values of displayedItems. ListAnimatedItems(data) } } }
Punti chiave del codice
ListAnimatedItemsExample
mostra una schermata che include i controlli per aggiungere, rimuovere e ordinare gli elementi.onAddItem
eonRemoveItem
sono espressioni lambda passate aAddRemoveButtons
per aggiungere e rimuovere elementi dall'elenco.resetOrder
,onSortAlphabetically
eonSortByLength
sono espressioni lambda che vengono passate aOrderButtons
per modificare l'ordine degli elementi nell'elenco.
AddRemoveButtons
vengono visualizzati i pulsanti "Aggiungi" e "Rimuovi". Attiva/disattiva i pulsanti e gestisce i clic sui pulsanti.OrderButtons
vengono visualizzati i pulsanti per riordinare l'elenco. Riceve le funzioni lambda per reimpostare l'ordine e ordinare l'elenco in base alla lunghezza o alfabeticamente.ListAnimatedItems
chiama il componibileListAnimatedItems
, passando l'elencodata
per visualizzare l'elenco animato di stringhe.data
è definito altrove.
Questo snippet crea un'interfaccia utente con i pulsanti Aggiungi elemento ed Elimina elemento:
@Composable private fun AddRemoveButtons( canAddItem: Boolean, canRemoveItem: Boolean, onAddItem: () -> Unit, onRemoveItem: () -> Unit ) { Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center ) { Button(enabled = canAddItem, onClick = onAddItem) { Text("Add Item") } Spacer(modifier = Modifier.padding(25.dp)) Button(enabled = canRemoveItem, onClick = onRemoveItem) { Text("Delete Item") } } }
Punti chiave del codice
AddRemoveButtons
mostra una riga di pulsanti per eseguire operazioni di aggiunta e rimozione nell'elenco.- I parametri
canAddItem
ecanRemoveItem
controllano lo stato di attivazione dei pulsanti. SecanAddItem
ocanRemoveItem
sono false, il pulsante corrispondente viene disattivato. - I parametri
onAddItem
eonRemoveItem
sono lambda che vengono eseguiti quando l'utente fa clic sul pulsante corrispondente.
Infine, questo snippet mostra tre pulsanti per ordinare l'elenco (Reimposta, alfabetico e Lunghezza):
@Composable private fun OrderButtons( resetOrder: () -> Unit, orderAlphabetically: () -> Unit, orderByLength: () -> Unit ) { Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center ) { var selectedIndex by remember { mutableIntStateOf(0) } val options = listOf("Reset", "Alphabetical", "Length") SingleChoiceSegmentedButtonRow { options.forEachIndexed { index, label -> SegmentedButton( shape = SegmentedButtonDefaults.itemShape( index = index, count = options.size ), onClick = { Log.d("AnimatedOrderedList", "selectedIndex: $selectedIndex") selectedIndex = index when (options[selectedIndex]) { "Reset" -> resetOrder() "Alphabetical" -> orderAlphabetically() "Length" -> orderByLength() } }, selected = index == selectedIndex ) { Text(label) } } } } }
Punti chiave del codice
OrderButtons
viene visualizzato unSingleChoiceSegmentedButtonRow
per consentire agli utenti di selezionare un metodo di ordinamento nell'elenco o reimpostare l'ordine dell'elenco. Un componenteSegmentedButton
ti consente di selezionare una singola opzione da un elenco di opzioni.resetOrder
,orderAlphabetically
eorderByLength
sono funzioni lambda che vengono eseguite quando viene selezionato il pulsante corrispondente.- La variabile di stato
selectedIndex
tiene traccia dell'opzione selezionata.
Risultato
Questo video mostra il risultato degli snippet precedenti quando gli elementi vengono riordinati:
Intestazioni fisse (sperimentale)
Il pattern "intestazione fissa" è utile per visualizzare elenchi di dati raggruppati. Di seguito puoi vedere un esempio di "elenco di contatti", raggruppato in base all'iniziale di ogni contatto:
Per ottenere un'intestazione fissa con LazyColumn
, puoi utilizzare la funzione sperimentale
stickyHeader()
fornendo i contenuti dell'intestazione:
@OptIn(ExperimentalFoundationApi::class) @Composable fun ListWithHeader(items: List<Item>) { LazyColumn { stickyHeader { Header() } items(items) { item -> ItemRow(item) } } }
Per creare un elenco con più intestazioni, come l'esempio "elenco contatti" riportato sopra, puoi:
// This ideally would be done in the ViewModel val grouped = contacts.groupBy { it.firstName[0] } @OptIn(ExperimentalFoundationApi::class) @Composable fun ContactsList(grouped: Map<Char, List<Contact>>) { LazyColumn { grouped.forEach { (initial, contactsForInitial) -> stickyHeader { CharacterHeader(initial) } items(contactsForInitial) { contact -> ContactListItem(contact) } } } }
Reagire alla posizione di scorrimento
Molte app devono reagire e ascoltare le modifiche alla posizione di scorrimento e al layout degli elementi.
I componenti Lazy supportano questo caso d'uso eseguendo il sollevamento del
LazyListState
:
@Composable fun MessageList(messages: List<Message>) { // Remember our own LazyListState val listState = rememberLazyListState() // Provide it to LazyColumn LazyColumn(state = listState) { // ... } }
Per casi d'uso semplici, in genere le app devono conoscere solo le informazioni sul primo elemento visibile. Per questo,
LazyListState
fornisce le proprietà
firstVisibleItemIndex
e
firstVisibleItemScrollOffset
.
Se utilizziamo l'esempio di un pulsante che viene mostrato e nascosto in base al fatto che l'utente abbia superato il primo elemento:
@Composable fun MessageList(messages: List<Message>) { Box { val listState = rememberLazyListState() LazyColumn(state = listState) { // ... } // Show the button if the first visible item is past // the first item. We use a remembered derived state to // minimize unnecessary compositions val showButton by remember { derivedStateOf { listState.firstVisibleItemIndex > 0 } } AnimatedVisibility(visible = showButton) { ScrollToTopButton() } } }
La lettura dello stato direttamente nella composizione è utile quando devi aggiornare altri composabili dell'interfaccia utente, ma esistono anche scenari in cui l'evento non deve essere gestito nella stessa composizione. Un esempio comune è l'invio di un
evento di analisi dopo che l'utente ha superato un determinato punto. Per gestire questo problema in modo efficiente, possiamo utilizzare un snapshotFlow()
:
val listState = rememberLazyListState() LazyColumn(state = listState) { // ... } LaunchedEffect(listState) { snapshotFlow { listState.firstVisibleItemIndex } .map { index -> index > 0 } .distinctUntilChanged() .filter { it } .collect { MyAnalyticsService.sendScrolledPastFirstItemEvent() } }
LazyListState
fornisce anche informazioni su tutti gli elementi attualmente visualizzati e sui relativi limiti sullo schermo tramite la proprietà layoutInfo
. Per ulteriori informazioni, consulta la classe
LazyListLayoutInfo
.
Controllare la posizione di scorrimento
Oltre a reagire alla posizione di scorrimento, è utile anche per le app poter controllare la posizione di scorrimento.
LazyListState
supporta questa funzionalità tramite la funzione scrollToItem()
, che "blocca immediatamente" la posizione di scorrimento e animateScrollToItem()
, che scorre utilizzando un'animazione (nota anche come scorrimento fluido):
@Composable fun MessageList(messages: List<Message>) { val listState = rememberLazyListState() // Remember a CoroutineScope to be able to launch val coroutineScope = rememberCoroutineScope() LazyColumn(state = listState) { // ... } ScrollToTopButton( onClick = { coroutineScope.launch { // Animate scroll to the first item listState.animateScrollToItem(index = 0) } } ) }
Set di dati di grandi dimensioni (paginazione)
La libreria di paginazione consente alle app di supportare elenchi di elementi di grandi dimensioni, caricando e visualizzando piccoli blocchi dell'elenco in base alle necessità. Paging 3.0 e versioni successive forniscono il supporto di Compose tramite la libreriaandroidx.paging:paging-compose
.
Per visualizzare un elenco di contenuti paginati, possiamo utilizzare la funzione di estensione
collectAsLazyPagingItems()
e poi passare il valore LazyPagingItems
riferito a items()
nel nostro LazyColumn
. Analogamente al supporto della paginazione nelle visualizzazioni, puoi visualizzare i segnaposto durante il caricamento dei dati controllando se item
è null
:
@Composable fun MessageList(pager: Pager<Int, Message>) { val lazyPagingItems = pager.flow.collectAsLazyPagingItems() LazyColumn { items( lazyPagingItems.itemCount, key = lazyPagingItems.itemKey { it.id } ) { index -> val message = lazyPagingItems[index] if (message != null) { MessageRow(message) } else { MessagePlaceholder() } } } }
Suggerimenti per l'utilizzo dei layout lazy
Esistono alcuni suggerimenti che puoi tenere in considerazione per assicurarti che i layout lazy funzionino come previsto.
Evita di utilizzare elementi di dimensioni pari a 0 pixel
Questo può accadere in scenari in cui, ad esempio, prevedi di recuperare in modo asincrono alcuni dati, come le immagini, per completare gli elementi dell'elenco in un secondo momento. In questo modo, il layout lazy comporrà tutti i suoi elementi nella prima misurazione, poiché la loro altezza è pari a 0 pixel e potrebbero essere inseriti tutti nell'area visibile. Una volta caricati gli elementi e aumentata la loro altezza, i layout lazy eliminano tutti gli altri elementi che sono stati composti inutilmente la prima volta perché non possono adattarsi all'area visibile. Per evitare questo problema, devi impostare le dimensioni predefinite per gli elementi, in modo che il layout lazy possa eseguire il calcolo corretto del numero di elementi che possono effettivamente essere visualizzati nell'area visibile:
@Composable fun Item(imageUrl: String) { AsyncImage( model = rememberAsyncImagePainter(model = imageUrl), modifier = Modifier.size(30.dp), contentDescription = null // ... ) }
Se conosci le dimensioni approssimative degli elementi dopo il caricamento asincrono dei dati, è buona norma assicurarti che rimangano invariate prima e dopo il caricamento, ad esempio aggiungendo alcuni segnaposto. In questo modo, manterrai la posizione di scorrimento corretta.
Evita di nidificare componenti scorrevoli nella stessa direzione
Questo vale solo per i casi in cui vengono nidificati elementi secondari scorrevoli senza una dimensione predefinita all'interno di un altro elemento principale scorrevole nella stessa direzione. Ad esempio, prova a nidificare un elemento secondario LazyColumn
senza altezza fissa all'interno di un elemento principale Column
scorrevole verticalmente:
// throws IllegalStateException Column( modifier = Modifier.verticalScroll(state) ) { LazyColumn { // ... } }
Invece, puoi ottenere lo stesso risultato inserendo tutti i composabili
in un elemento principale LazyColumn
e utilizzando il relativo DSL per trasmettere diversi tipi di
contenuti. In questo modo puoi emettere singoli elementi e più elementi dell'elenco,
tutto in un'unica posizione:
LazyColumn { item { Header() } items(data) { item -> PhotoItem(item) } item { Footer() } }
Tieni presente che sono consentiti casi in cui nidifichi layout con direzioni diverse, ad esempio un elemento principale Row
scorrevole e un elemento secondario LazyColumn
:
Row( modifier = Modifier.horizontalScroll(scrollState) ) { LazyColumn { // ... } }
nonché nei casi in cui utilizzi ancora gli stessi layout di direzione, ma imposti anche una dimensione fissa per gli elementi secondari nidificati:
Column( modifier = Modifier.verticalScroll(scrollState) ) { LazyColumn( modifier = Modifier.height(200.dp) ) { // ... } }
Fai attenzione a non inserire più elementi in un elemento
In questo esempio, il secondo elemento lambda emette 2 elementi in un blocco:
LazyVerticalGrid( columns = GridCells.Adaptive(100.dp) ) { item { Item(0) } item { Item(1) Item(2) } item { Item(3) } // ... }
I layout lazy gestiranno questo problema come previsto: disporranno gli elementi uno dopo l'altro come se fossero elementi diversi. Tuttavia, esistono alcuni problemi.
Quando vengono emessi più elementi all'interno di un elemento, vengono gestiti come un'unica entità, il che significa che non possono più essere composti singolarmente. Se un elemento diventa visibile sullo schermo, tutti gli elementi corrispondenti devono essere composti e misurati. Ciò può influire negativamente sul rendimento se usato
in modo eccessivo. Nel caso estremo di mettere tutti gli elementi in un elemento, viene completamente vanificato lo scopo dell'utilizzo dei layout lazy. Oltre ai potenziali problemi di rendimento, l'inserimento di più elementi in un elemento interferirà anche con scrollToItem()
e animateScrollToItem()
.
Tuttavia, esistono casi d'uso validi per inserire più elementi in un elemento, come avere divisori all'interno di un elenco. Non vuoi che i separatori modifichino gli indici di scorrimento, poiché non devono essere considerati elementi indipendenti. Inoltre, il rendimento non sarà interessato perché i divisori sono piccoli. È probabile che un divisore debba essere visualizzato quando è visibile l'elemento precedente, in modo da poter far parte dell'elemento precedente:
LazyVerticalGrid( columns = GridCells.Adaptive(100.dp) ) { item { Item(0) } item { Item(1) Divider() } item { Item(2) } // ... }
Valuta la possibilità di utilizzare arrangiamenti personalizzati
In genere, gli elenchi lazy contengono molti elementi e occupano più delle dimensioni del contenitore scorrevole. Tuttavia, quando l'elenco è compilato con pochi elementi, il design può avere requisiti più specifici per il posizionamento degli elementi nel viewport.
Per farlo, puoi utilizzare il verticale personalizzato
Arrangement
e passarlo a LazyColumn
. Nell'esempio seguente, l'oggetto TopWithFooter
deve implementare solo il metodo arrange
. Innanzitutto, posizionerà gli elementi uno dopo l'altro. In secondo luogo, se l'altezza totale utilizzata è inferiore all'altezza del viewport, il piè di pagina verrà posizionato in basso:
object TopWithFooter : Arrangement.Vertical { override fun Density.arrange( totalSize: Int, sizes: IntArray, outPositions: IntArray ) { var y = 0 sizes.forEachIndexed { index, size -> outPositions[index] = y y += size } if (y < totalSize) { val lastIndex = outPositions.lastIndex outPositions[lastIndex] = totalSize - sizes.last() } } }
Valuta la possibilità di aggiungere contentType
A partire da Compose 1.2, per massimizzare il rendimento del layout Lazy, ti consigliamo di aggiungere contentType
ai tuoi elenchi o alle tue griglie. In questo modo puoi specificare il tipo di contenuti per ogni elemento del layout, nei casi in cui stai componendo un elenco o una griglia composta da più tipi di elementi diversi:
LazyColumn { items(elements, contentType = { it.type }) { // ... } }
Quando fornisci il valore contentType
, Compose è in grado di riutilizzare le composizioni solo tra gli elementi dello stesso tipo. Poiché il riutilizzo è più efficiente quando componi elementi di struttura simile, fornire i tipi di contenuti garantisce che Compose non provi a comporre un elemento di tipo A sopra un elemento di tipo B completamente diverso. In questo modo, puoi massimizzare i vantaggi del riutilizzo della composizione e il rendimento del layout lazy.
Misurare il rendimento
Puoi misurare in modo affidabile il rendimento di un layout lazy solo quando esegui il codice in modalità di rilascio e con l'ottimizzazione R8 abilitata. Nelle build di debug, lo scorrimento del layout lazy potrebbe sembrare più lento. Per ulteriori informazioni, consulta Rendimento di Compose.
Consigliati per te
- Nota: il testo del link viene visualizzato quando JavaScript è disattivato
- Eseguire la migrazione di
RecyclerView
in un elenco lazy - Salvare lo stato dell'interfaccia utente in Scrivi
- Kotlin per Jetpack Compose