Molte app devono visualizzare raccolte di elementi. Questo documento spiega come puoi farlo 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 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 dispongono gli elementi e scorrono. 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 di blocco dei contenuti @Composable
, che consente alle app di emettere direttamente i componenti componibili, i componenti Lazy forniscono un blocco LazyListScope.()
. Questo
blocco LazyListScope
offre un DSL che consente alle app di descrivere i contenuti dell'elemento. Il componente
Lazy è quindi responsabile dell'aggiunta dei contenuti di ogni elemento in base
ai requisiti del layout e alla 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 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 maggiori dettagli, consulta
il riferimento
LazyListScope
.
Griglie lazy
I composable
LazyVerticalGrid
e
LazyHorizontalGrid
forniscono supporto per la visualizzazione degli elementi in una griglia. Una griglia verticale pigra
mostrerà i suoi elementi in un contenitore scorrevole verticalmente, distribuito su
più colonne, mentre le griglie orizzontali pigre 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 sia larga 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
inserirà il maggior numero possibile di colonne. La larghezza rimanente viene distribuita equamente
tra le colonne, dopo il calcolo del numero di colonne.
Questo modo adattivo di dimensionamento è particolarmente utile per visualizzare set 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 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 dello span, è particolarmente utile quando utilizzi
il dimensionamento adattivo, perché il numero di colonne non è fisso.
Questo esempio mostra come fornire un intervallo di riga completo:
LazyVerticalGrid( columns = GridCells.Adaptive(minSize = 30.dp) ) { item(span = { // LazyGridItemSpanScope: // maxLineSpan GridItemSpan(maxLineSpan) }) { CategoryCard("Fruits") } // ... }
Griglia sfalsata pigra
LazyVerticalStaggeredGrid
e
LazyHorizontalStaggeredGrid
sono composable che ti consentono di creare una griglia sfalsata di elementi caricati in modalità differita.
Una griglia verticale sfalsata pigra mostra i suoi elementi in un contenitore a scorrimento verticale che si estende su più colonne e consente ai singoli elementi di avere altezze diverse. Le griglie orizzontali pigre hanno lo stesso comportamento sull'asse orizzontale con elementi di larghezza diversa.
Lo snippet seguente è 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() )

Spazio interno dei contenuti
A volte è necessario aggiungere un riempimento intorno ai bordi dei contenuti. I componenti
lazy ti consentono di passare alcuni
PaddingValues
al parametro contentPadding
per supportare questa operazione:
LazyColumn( contentPadding = PaddingValues(horizontal = 16.dp, vertical = 8.dp), ) { // ... }
In questo esempio, aggiungiamo 16.dp
di spaziatura interna ai bordi orizzontali (sinistro e
destro), quindi 8.dp
alla parte superiore e inferiore dei contenuti.
Tieni presente che questo spazio interno viene applicato ai contenuti, non al
LazyColumn
stesso. Nell'esempio precedente, il primo elemento aggiungerà un riempimento 8.dp
alla parte superiore, l'ultimo elemento aggiungerà un riempimento 8.dp
alla parte inferiore e tutti gli elementi
avranno un riempimento 16.dp
a sinistra e a destra.
Come altro esempio, puoi passare Scaffold
's PaddingValues
in
LazyColumn
's contentPadding
. Consulta la guida
da bordo a bordo.
Spaziatura dei contenuti
Per aggiungere spazio tra gli elementi, puoi utilizzare
Arrangement.spacedBy()
.
L'esempio seguente aggiunge 4.dp
di spazio tra un elemento e l'altro:
LazyColumn( verticalArrangement = Arrangement.spacedBy(4.dp), ) { // ... }
Analogamente per LazyRow
:
LazyRow( horizontalArrangement = Arrangement.spacedBy(4.dp), ) { // ... }
Le griglie, invece, accettano sia disposizioni verticali che 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 viene associato alla posizione dell'elemento nell'elenco o nella griglia. Tuttavia, ciò può causare problemi se il set di dati cambia, poiché gli elementi che
cambiano posizione perdono effettivamente qualsiasi stato memorizzato. Se immagini lo
scenario di LazyRow
all'interno di un LazyColumn
, se la riga cambia la posizione dell'elemento,
l'utente perderebbe la posizione di scorrimento all'interno della riga.
Per contrastare questo problema, puoi fornire una chiave stabile e univoca per ogni elemento, fornendo
un blocco al parametro key
. Fornire una chiave stabile consente di mantenere lo stato dell'elemento
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 riordini. Ad esempio, se l'elemento contiene lo 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,
enumerazioni 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 lontano da questo elemento e poi 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
degli elementi.
I layout pigri forniscono la stessa funzionalità per il riordino degli elementi.
L'API è semplice: devi solo impostare il modificatore
animateItem
per i 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 specifiche di animazione personalizzate:
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: animare gli elementi negli elenchi pigri
Con Compose, puoi animare le modifiche agli elementi negli elenchi pigri. Se utilizzati insieme, i seguenti snippet implementano le animazioni quando si aggiungono, rimuovono e riordinano gli elementi delle liste pigre.
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 le chiavi per monitorare gli elementi e identificare le modifiche alle loro posizioni. ListItem
definisce il layout di ogni elemento dell'elenco. Accetta un parametroheadlineContent
che definisce i contenuti principali dell'elemento.- Il modificatore
animateItem
applica le animazioni predefinite alle aggiunte, alle rimozioni e agli spostamenti degli elementi.
Il seguente snippet mostra una schermata che include 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 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 trasmesse 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
mostra i pulsanti per riordinare l'elenco. Riceve le funzioni lambda per reimpostare l'ordine e ordinare l'elenco per lunghezza o in ordine alfabetico.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 è 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 Durata):
@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
mostra unSingleChoiceSegmentedButtonRow
che consente 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", raggruppati in base all'iniziale di ciascun 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 ottenere un elenco con più intestazioni, come nell'esempio dell'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 tramite 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, le app in genere 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 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 composable dell'interfaccia utente, ma ci sono anche scenari in cui l'evento non deve
essere gestito nella stessa composizione. Un esempio comune è l'invio di un
evento Analytics quando l'utente ha superato un determinato 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 il corso
LazyListLayoutInfo
.
Controllare la posizione di scorrimento
Oltre a reagire alla posizione di scorrimento, è utile anche che le app possano
controllare la posizione di scorrimento.
LazyListState
lo supporta tramite la funzione scrollToItem()
,
che sposta "immediatamente" la
posizione di scorrimento, e animateScrollToItem()
,
che scorre utilizzando un'animazione (nota anche come scorrimento uniforme):
@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 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
libreria androidx.paging:paging-compose
.
Per visualizzare un elenco di contenuti suddivisi in pagine, possiamo utilizzare la funzione di estensione
collectAsLazyPagingItems()
e quindi passare il valore LazyPagingItems
restituito
a items()
in 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 pigri
Esistono alcuni suggerimenti che puoi prendere in considerazione per assicurarti che i layout pigri funzionino come previsto.
Evita di utilizzare elementi di dimensioni pari a 0 pixel
Ciò può verificarsi 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 inserirli tutti nell'area visibile. Una volta caricati gli elementi e la loro altezza espansa, i layout pigri scartano tutti gli altri elementi che sono stati composti inutilmente la prima volta, in quanto non possono adattarsi all'area visibile. Per evitare questo problema, devi impostare il dimensionamento predefinito per gli elementi, in modo che il layout pigro possa eseguire il calcolo corretto di 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 // ... ) }
Quando conosci le dimensioni approssimative degli elementi dopo il caricamento asincrono dei dati, una buona pratica è assicurarsi 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
Ciò si applica solo ai casi in cui si annidano elementi secondari scorrevoli senza una dimensione predefinita
all'interno di un altro elemento principale scorrevole nella stessa direzione. Ad esempio, se provi a
inserire un elemento secondario LazyColumn
senza un'altezza fissa all'interno di un elemento principale
Column
scorrevole verticalmente:
// throws IllegalStateException Column( modifier = Modifier.verticalScroll(state) ) { LazyColumn { // ... } }
Lo stesso risultato può essere ottenuto racchiudendo tutti i composable
all'interno di un LazyColumn
principale e utilizzando il relativo DSL per passare diversi tipi di
contenuti. Ciò consente di emettere singoli elementi, nonché più elementi di 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 di direzione diversi,
ad esempio un elemento principale scorrevole Row
e un elemento secondario LazyColumn
:
Row( modifier = Modifier.horizontalScroll(scrollState) ) { LazyColumn { // ... } }
Oltre ai 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 lambda del secondo elemento emette due elementi in un blocco:
LazyVerticalGrid( columns = GridCells.Adaptive(100.dp) ) { item { Item(0) } item { Item(1) Item(2) } item { Item(3) } // ... }
I layout pigri gestiranno questo aspetto come previsto: disporranno gli elementi uno dopo l'altro come se fossero elementi diversi. Tuttavia, ci sono un paio di problemi.
Quando più elementi vengono emessi come parte di un singolo elemento, vengono gestiti come
una sola 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, può influire negativamente sul rendimento. Nel caso estremo di inserimento di tutti gli elementi in un unico elemento, lo scopo dell'utilizzo dei layout pigri viene completamente vanificato. Oltre a 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, ad esempio i 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 saranno interessate perché i divisori sono piccoli. Un divisore dovrà probabilmente essere visibile quando l'elemento precedente è visibile, in modo che possa 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
Di solito, gli elenchi pigri contengono molti elementi e occupano uno spazio maggiore rispetto alle dimensioni del contenitore di scorrimento. Tuttavia, quando l'elenco è popolato da pochi elementi, il design può avere requisiti più specifici per il posizionamento nel riquadro visibile.
Per farlo, puoi utilizzare un 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 riquadro 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 il rendimento del layout Lazy, valuta la possibilità di aggiungere contentType
a elenchi 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 costituita da più tipi diversi di elementi:
LazyColumn { items(elements, contentType = { it.type }) { // ... } }
Quando fornisci
contentType
,
Compose è in grado di riutilizzare le composizioni solo
tra gli elementi dello stesso tipo. Il riutilizzo è più efficiente quando componi elementi di struttura simile. La fornitura dei tipi di contenuti garantisce che Compose non tenti di comporre un elemento di tipo A sopra un elemento completamente diverso di tipo B. 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 viene eseguito 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 Rendimento della composizione.
Risorse aggiuntive
- Creare un elenco scorrevole finito
- Creare una griglia scorrevole
- Visualizzare gli elementi di scorrimento nidificati in un elenco
- Filtrare un elenco durante la digitazione
- Caricare i dati in modo differito con elenchi e paginazione
- Creare un elenco utilizzando più tipi di elementi
- Video: Elenchi in Composizione
Consigliati per te
- Nota: il testo del link viene visualizzato quando JavaScript è disattivato
- Eseguire la migrazione di
RecyclerView
all'elenco pigro - Salvare lo stato dell'interfaccia utente in Compose
- Kotlin per Jetpack Compose