Molte app devono mostrare raccolte di elementi. Questo documento spiega come farlo in modo efficiente in Jetpack Compose.
Se sai che il tuo caso d'uso non richiede lo scorrimento, puoi utilizzare un semplice Column o Row (a seconda della direzione) ed emettere i contenuti di ogni elemento eseguendo l'iterazione su un elenco nel seguente modo:
@Composable fun MessageList(messages: List<Message>) { Column { messages.forEach { message -> MessageRow(message) } } }
Possiamo rendere scorrevole 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 dispongono 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
produce un elenco a scorrimento verticale, mentre LazyRow produce un elenco a scorrimento orizzontale.
I componenti Lazy sono diversi dalla maggior parte dei layout in Compose. Anziché accettare un parametro del blocco di contenuti @Composable, consentendo alle app di emettere direttamente elementi componibili, 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, come richiesto dal layout e dalla posizione di scorrimento.
LazyListScope DSL
Il DSL di LazyListScope fornisce una serie di funzioni per descrivere gli elementi nel layout. Nella sua 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 diverse funzioni di estensione che consentono di aggiungere raccolte di elementi, ad esempio un List. Queste estensioni ci consentono di eseguire facilmente la migrazione del nostro 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 di LazyListScope.
Griglie lazy
I componibili LazyVerticalGrid e LazyHorizontalGrid forniscono il supporto per la visualizzazione degli elementi in una griglia. Una griglia verticale lazy
visualizzerà i suoi elementi in un contenitore scorrevole verticalmente, esteso su
più colonne, mentre le griglie orizzontali lazy avranno lo stesso comportamento
sull'asse orizzontale.
Le griglie hanno le stesse potenti funzionalità API degli elenchi e utilizzano anche un DSL molto simile, LazyGridScope.(), per descrivere i contenuti.
Il parametro columns in
LazyVerticalGrid
e il parametro rows in
LazyHorizontalGrid
controllano il modo in cui le celle vengono formate in colonne o righe. L'esempio seguente mostra gli elementi in una griglia, utilizzando GridCells.Adaptive per impostare ogni colonna in modo che abbia una larghezza di almeno 128.dp:
LazyVerticalGrid( columns = GridCells.Adaptive(minSize = 128.dp) ) { items(photos) { photo -> PhotoItem(photo) } }
LazyVerticalGrid ti consente di specificare una larghezza per gli elementi, quindi la griglia si adatterà al maggior numero possibile di colonne. La larghezza rimanente viene distribuita equamente tra le colonne, dopo aver calcolato il numero di colonne.
Questo modo adattivo di dimensionare è 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 determinati elementi abbiano dimensioni non standard, puoi utilizzare il supporto della griglia per fornire estensioni di colonna personalizzate 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 estensione, è particolarmente utile quando utilizzi
il dimensionamento adattivo, perché il numero di colonne non è fisso.
Questo esempio mostra come fornire un'estensione di riga completa:
LazyVerticalGrid( columns = GridCells.Adaptive(minSize = 30.dp) ) { item(span = { // LazyGridItemSpanScope: // maxLineSpan GridItemSpan(maxLineSpan) }) { CategoryCard("Fruits") } // ... }
Griglia sfalsata lazy
LazyVerticalStaggeredGrid
e
LazyHorizontalStaggeredGrid
sono componibili che ti consentono di creare una griglia di elementi con caricamento lento e sfalsata.
Una griglia verticale con layout sfalsato con caricamento lento 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 larghezze diverse.
Il seguente snippet è un esempio di base di 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 utilizzare StaggeredGridCells.Fixed(columns) anziché StaggeredGridCells.Adaptive.
In questo modo, la larghezza disponibile viene divisa per il numero di colonne (o righe per una griglia orizzontale) e ogni elemento occupa quella 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() )
Spaziatura interna dei contenuti
A volte è necessario aggiungere spaziatura interna intorno ai bordi dei contenuti. I componenti lazy ti 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 interna ai bordi orizzontali (sinistro e destro) e poi 8.dp alla parte superiore e inferiore dei contenuti.
Tieni presente che questo padding viene applicato ai contenuti, non alla LazyColumn stessa. Nell'esempio precedente, il primo elemento aggiungerà un padding di 8.dp
nella parte superiore, l'ultimo elemento aggiungerà un padding di 8.dp nella parte inferiore e tutti gli elementi
avranno un padding di 16.dp a sinistra e a destra.
Un altro esempio è il passaggio di PaddingValues di Scaffold in contentPadding di LazyColumn. Consulta la guida edge-to-edge.
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 disposizioni sia verticali che orizzontali:
LazyVerticalGrid( columns = GridCells.Fixed(2), verticalArrangement = Arrangement.spacedBy(16.dp), horizontalArrangement = Arrangement.spacedBy(16.dp) ) { items(photos) { item -> PhotoItem(item) } }
Chiavi degli elementi
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 cambia la posizione dell'elemento, l'utente perde la posizione di scorrimento all'interno della riga.
Per evitare questo problema, puoi fornire una chiave univoca e stabile per ogni elemento, fornendo un blocco al parametro key. Fornire una chiave stabile consente di mantenere lo stato degli elementi coerente tra le 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 riordinamenti. Ad esempio, se l'elemento contiene uno stato memorizzato, l'impostazione delle chiavi consentirebbe 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 sui tipi che puoi utilizzare come chiavi degli elementi.
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 primitive, enum o Parcelable.
LazyColumn { items(books, key = { // primitives, enums, Parcelable, etc. }) { // ... } }
La chiave deve essere supportata da Bundle in modo che rememberSaveable all'interno
del composable dell'elemento possa essere ripristinato quando l'attività viene ricreata o anche
quando scorri via 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 automaticamente le modifiche agli elementi.
I layout lazy offrono la stessa funzionalità per il riordinamento degli elementi.
L'API è semplice: devi solo impostare il modificatore animateItem sui contenuti dell'elemento:
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 le chiavi per i tuoi elementi in modo che sia possibile trovare la nuova posizione dell'elemento spostato.
Esempio: anima gli elementi negli elenchi lazy
Con Compose, puoi animare le modifiche agli elementi negli elenchi con caricamento lento. Se utilizzati insieme, i seguenti snippet implementano le animazioni durante l'aggiunta, la rimozione e il riordinamento degli elementi di un 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 sul codice
ListAnimatedItemsmostra un elenco di stringhe in unLazyColumncon transizioni animate quando gli elementi vengono modificati.- La funzione
itemsassegna una chiave univoca a ogni elemento dell'elenco. Compose utilizza le chiavi per monitorare gli elementi e identificare le modifiche alle loro posizioni. ListItemdefinisce il layout di ogni elemento dell'elenco. Accetta un parametroheadlineContent, che definisce i contenuti principali dell'elemento.- Il modificatore
animateItemapplica animazioni predefinite alle aggiunte, alle rimozioni e agli spostamenti degli elementi.
Il seguente snippet presenta una schermata che incorpora i 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 sul codice
ListAnimatedItemsExamplepresenta una schermata che incorpora i controlli per aggiungere, rimuovere e ordinare gli elementi.onAddItemeonRemoveItemsono espressioni lambda che vengono passate aAddRemoveButtonsper aggiungere e rimuovere elementi dall'elenco.resetOrder,onSortAlphabeticallyeonSortByLengthsono espressioni lambda che vengono passate aOrderButtonsper modificare l'ordine degli elementi nell'elenco.
AddRemoveButtonsmostra i pulsanti "Aggiungi" e "Rimuovi". Attiva/disattiva i pulsanti e gestisce i clic sui pulsanti.OrderButtonsmostra i pulsanti per riordinare l'elenco. Riceve le funzioni lambda per reimpostare l'ordine e ordinare l'elenco in base alla lunghezza o in ordine alfabetico.ListAnimatedItemschiama il componibileListAnimatedItems, passando l'elencodataper visualizzare l'elenco animato di stringhe.dataè definito altrove.
Questo snippet crea una UI 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 sul codice
AddRemoveButtonsmostra una riga di pulsanti per eseguire operazioni di aggiunta e rimozione nell'elenco.- I parametri
canAddItemecanRemoveItemcontrollano lo stato di attivazione dei pulsanti. SecanAddItemocanRemoveItemsono impostati su false, il pulsante corrispondente è disattivato. - I parametri
onAddItemeonRemoveItemsono 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 sul codice
OrderButtonsmostra unSingleChoiceSegmentedButtonRowper consentire agli utenti di selezionare un metodo di ordinamento nell'elenco o di reimpostare l'ordine dell'elenco. Un componenteSegmentedButtonti consente di selezionare una singola opzione da un elenco di opzioni.resetOrder,orderAlphabeticallyeorderByLengthsono funzioni lambda che vengono eseguite quando viene selezionato il pulsante corrispondente.- La variabile di stato
selectedIndextiene traccia dell'opzione selezionata.
Risultato
Questo video mostra il risultato degli snippet precedenti quando gli elementi vengono riordinati:
Intestazioni fisse
Il pattern "intestazione fissa" è utile per visualizzare elenchi di dati raggruppati. Di seguito è riportato un esempio di "elenco contatti", raggruppato in base all'iniziale di ogni contatto:

Per ottenere un'intestazione fissa con LazyColumn, puoi utilizzare
stickyHeader()
fornendo i contenuti dell'intestazione:
@Composable fun ListWithHeader(items: List<Item>) { LazyColumn { stickyHeader { Header() } items(items) { item -> ItemRow(item) } } }
Per ottenere un elenco con più intestazioni, come nell'esempio "elenco contatti" riportato sopra, puoi procedere come segue:
// This ideally would be done in the ViewModel val grouped = contacts.groupBy { it.firstName[0] } @Composable fun ContactsList(grouped: Map<Char, List<Contact>>) { LazyColumn { grouped.forEach { (initial, contactsForInitial) -> stickyHeader { CharacterHeader(initial) } items(contactsForInitial) { contact -> ContactListItem(contact) } } } }
Reazione 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 l'hoisting di
LazyListState:
@Composable fun MessageList(messages: List<Message>) { // Remember our own LazyListState val listState = rememberLazyListState() // Provide it to LazyColumn LazyColumn(state = listState) { // ... } }
Per i casi d'uso semplici, in genere le app devono conoscere solo le informazioni relative al primo elemento visibile. A questo scopo,
LazyListState
fornisce le proprietà
firstVisibleItemIndex
e
firstVisibleItemScrollOffset.
Se utilizziamo l'esempio di visualizzazione e nascondimento di un pulsante in base allo scorrimento dell'utente oltre 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 componibili 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 una volta che l'utente ha fatto scorrere la pagina oltre un certo punto. Per gestire questa situazione 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 saperne di più, consulta la classe LazyListLayoutInfo.
Controllo della posizione di scorrimento
Oltre a reagire alla posizione di scorrimento, è utile anche che le app siano in grado di controllare la posizione di scorrimento.
LazyListState
supporta questa funzionalità tramite la funzione scrollToItem(),
che aggancia "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 (impaginazione)
La libreria Paging consente alle app di supportare elenchi di elementi di grandi dimensioni, caricando e visualizzando piccole parti dell'elenco in base alle necessità. Paging 3.0 e versioni successive fornisce il supporto di Compose tramite la libreria androidx.paging:paging-compose.
Per visualizzare un elenco di contenuti impaginati, possiamo utilizzare la funzione di estensione collectAsLazyPagingItems() e poi passare l'oggetto LazyPagingItems restituito 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
Ecco 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 compilare gli elementi dell'elenco in un secondo momento. In questo modo, il layout Lazy comporrebbe tutti i suoi elementi nella prima misurazione, poiché la loro altezza è di 0 pixel e potrebbe adattarli tutti alla finestra visibile. Una volta caricati gli elementi e ampliata la loro altezza, i layout Lazy scartano tutti gli altri elementi che sono stati composti inutilmente la prima volta, in quanto non possono effettivamente adattarsi alla viewport. Per evitare questo problema, devi impostare le dimensioni predefinite per gli elementi, in modo che il layout Lazy possa calcolare correttamente quanti elementi possono effettivamente essere inseriti 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, una buona prassi è assicurarti che le dimensioni degli elementi rimangano invariate prima e dopo il caricamento, ad esempio aggiungendo alcuni segnaposto. In questo modo, la posizione di scorrimento corretta verrà mantenuta.
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, se provi a nidificare un elemento secondario LazyColumn senza un'altezza fissa all'interno di un elemento principale Column con scorrimento verticale:
// throws IllegalStateException Column( modifier = Modifier.verticalScroll(state) ) { LazyColumn { // ... } }
Invece, lo stesso risultato può essere ottenuto aggregando tutti i composable all'interno di un unico LazyColumn principale e utilizzando il relativo DSL per passare diversi tipi di contenuti. In questo modo è possibile emettere singoli elementi, nonché più elementi di un elenco, tutto in un unico posto:
LazyColumn { item { Header() } items(data) { item -> PhotoItem(item) } item { Footer() } }
Tieni presente che sono consentiti i casi in cui nidifichi layout con direzioni diverse, ad esempio un elemento principale scorrevole Row e un elemento secondario LazyColumn:
Row( modifier = Modifier.horizontalScroll(scrollState) ) { LazyColumn { // ... } }
Inoltre, 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 unico elemento
In questo esempio, la seconda funzione lambda degli elementi genera due 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 questa situazione come previsto: disporranno gli elementi uno dopo l'altro come se fossero elementi diversi. Tuttavia, questa operazione presenta un paio di problemi.
Quando vengono emessi più elementi come parte 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 all'elemento devono essere composti e misurati. Se utilizzato in modo eccessivo, questo può influire negativamente sulle prestazioni. Nel caso estremo di inserire tutti gli elementi in un unico elemento, lo scopo di utilizzare i layout Lazy viene completamente vanificato. Oltre ai potenziali problemi di prestazioni, l'inserimento di più elementi in un unico elemento interferirà anche con scrollToItem() e animateScrollToItem().
Tuttavia, esistono casi d'uso validi per inserire più elementi in un unico elemento, come l'inserimento di divisori all'interno di un elenco. Non vuoi che i divisori cambino gli indici di scorrimento, in quanto non devono essere considerati elementi indipendenti. Inoltre, le prestazioni non vengono influenzate perché i divisori sono piccoli. È probabile che un divisore debba essere visibile quando l'elemento precedente è visibile, quindi può 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 disposizioni personalizzate
In genere, gli elenchi Lazy contengono molti elementi e occupano più spazio rispetto alle dimensioni del contenitore di scorrimento. Tuttavia, se l'elenco contiene pochi elementi, il tuo design potrebbe avere requisiti più specifici per il loro posizionamento nell'area visibile.
Per farlo, puoi utilizzare la verticale personalizzata
Arrangement
e passarla 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 dell'area visibile, 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 le prestazioni del layout Lazy, valuta la possibilità di aggiungere
contentType
alle tue liste o 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
contentType,
Compose è in grado di riutilizzare le composizioni solo
tra gli elementi dello stesso tipo. Poiché il riutilizzo è più efficiente quando componi elementi con una struttura simile, fornire i tipi di contenuti garantisce che Compose non tenti di 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 le prestazioni del layout Lazy.
Misurazione delle prestazioni
Puoi misurare in modo affidabile le prestazioni di un layout Lazy solo quando l'esecuzione avviene in modalità di rilascio e con l'ottimizzazione R8 abilitata. Nelle build di debug, lo scorrimento del layout lazy potrebbe apparire più lento. Per ulteriori informazioni, leggi Prestazioni di Compose.
Risorse aggiuntive
- Crea un elenco scorrevole finito
- Crea una griglia scorrevole
- Visualizza gli elementi di scorrimento nidificati in un elenco
- Filtra un elenco durante la digitazione
- Carica i dati in modo lazy con elenchi e Paging
- Crea un elenco utilizzando più tipi di articolo
- Video: Elenchi in Compose
Consigliati per te
- Nota: il testo del link viene visualizzato quando JavaScript è disattivato
- Esegui la migrazione di
RecyclerViewa Lazy list - Salva lo stato della UI in Compose
- Kotlin per Jetpack Compose