Molte app devono mostrare 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, puoi utilizzare un semplice Column o Row (a seconda della direzione) ed emettere i contenuti di ogni elemento scorrendo 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 elementi 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
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 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 maggiori dettagli, consulta il riferimento
LazyListScope.
Griglie pigre
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'estensione di riga completa:
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 container scorrevole verticalmente
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.
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() )
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à 8.dp
al suo margine superiore, l'ultimo elemento aggiungerà 8.dp al suo margine inferiore e tutti gli elementi
avranno un margine di 16.dp a sinistra e a destra.
Come altro esempio, puoi passare PaddingValues di Scaffold in contentPadding di LazyColumn. Consulta la guida
da bordo a bordo.
Spaziatura dei contenuti
Per aggiungere spazio tra gli elementi, puoi utilizzare
Arrangement.spacedBy().
L'esempio riportato di seguito aggiunge 4.dp di spazio tra un elemento e l'altro:
LazyColumn( verticalArrangement = Arrangement.spacedBy(4.dp), ) { // ... }
Allo stesso modo per LazyRow:
LazyRow( horizontalArrangement = Arrangement.spacedBy(4.dp), ) { // ... }
Le griglie, tuttavia, accettano sia la disposizione verticale che orizzontale:
LazyVerticalGrid( columns = GridCells.Fixed(2), verticalArrangement = Arrangement.spacedBy(16.dp), horizontalArrangement = Arrangement.spacedBy(16.dp) ) { items(photos) { item -> PhotoItem(item) } }
Chiavi dell'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 LazyColumn, se la riga cambia la posizione dell'elemento, l'utente perde 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 degli elementi coerente tra le modifiche al 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 primitivi,
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 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 pigri forniscono la stessa funzionalità per il riordino degli elementi.
L'API è semplice: devi solo impostare il modificatore
animateItem
sul contenuto 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 vengono aggiunti, rimossi e riordinati gli elementi di elenco pigri.
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 le animazioni predefinite ad aggiunte, rimozioni e spostamenti di elementi.
Il seguente snippet mostra 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
ListAnimatedItemsExamplemostra una schermata che include i controlli per aggiungere, rimuovere e ordinare gli elementi.onAddItemeonRemoveItemsono espressioni lambda passate aAddRemoveButtonsper aggiungere e rimuovere elementi dall'elenco.resetOrder,onSortAlphabeticallyeonSortByLengthsono espressioni lambda passate aOrderButtonsper modificare l'ordine degli elementi nell'elenco.
AddRemoveButtonsvengono visualizzati 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 per lunghezza o in ordine alfabetico.ListAnimatedItemschiama il componibileListAnimatedItems, passando l'elencodataper 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 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 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 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 sul codice
OrderButtonsmostra unSingleChoiceSegmentedButtonRowche consente agli utenti di selezionare un metodo di ordinamento nell'elenco o 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 persistenti
Il pattern "intestazione fissa" è utile per visualizzare elenchi di dati raggruppati. Di seguito puoi vedere un esempio di "elenco contatti", raggruppato in base all'iniziale di ogni contatto:

Per ottenere un'intestazione fissa con LazyColumn, puoi utilizzare
stickyHeader()
funzione, 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 dell'elenco contatti riportato sopra, puoi:
// 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 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 mostrare e nascondere un pulsante in base al fatto che l'utente abbia scorri 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 una volta 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 saperne di più, consulta la classe 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
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 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 di impaginazione 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 impaginati, 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ò 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 inserirli tutti nella visualizzazione. 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 essere visualizzati nel riquadro. 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 visualizzati 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 dei tuoi 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ò vale solo per i 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. In questo modo è possibile 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 i figli 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 questa situazione 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 inserire tutti gli elementi in un unico elemento, lo scopo di utilizzare i 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, il rendimento non è interessato perché i divisori sono piccoli. Un divisore probabilmente deve 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 disposizioni personalizzate
Di solito, gli elenchi pigri contengono molti elementi e occupano più spazio del contenitore di scorrimento. Tuttavia, quando l'elenco è popolato da pochi elementi, il tuo 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 componi 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 pigro.
Misurazione delle prestazioni
Puoi misurare in modo affidabile le prestazioni 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 pigro potrebbe sembrare più lento. Per ulteriori informazioni, leggi Rendimento della composizione.
Risorse aggiuntive
- Creare un elenco scorrevole finito
- Creare una griglia scorrevole
- Visualizzare elementi scorrevoli 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 Compose
Consigliati per te
- Nota: il testo del link viene visualizzato quando JavaScript è disattivato
- Eseguire la migrazione di
RecyclerViewall'elenco pigro - Salvare lo stato dell'interfaccia utente in Compose
- Kotlin per Jetpack Compose