Listen und Raster

In vielen Apps müssen Sammlungen von Elementen angezeigt werden. In diesem Dokument wird erläutert, wie Sie dies effizient in Jetpack Compose erledigen können.

Wenn Sie wissen, dass für Ihren Anwendungsfall kein Scrollen erforderlich ist, können Sie ein einfaches Column oder Row (je nach Richtung) verwenden und den Inhalt jedes Elements ausgeben, indem Sie eine Liste wie diese durchlaufen:

@Composable
fun MessageList(messages: List<Message>) {
    Column {
        messages.forEach { message ->
            MessageRow(message)
        }
    }
}

Mit dem verticalScroll()-Modifikator können wir Column scrollbar machen. Weitere Informationen finden Sie in der Dokumentation zu Gesten.

Lazy Listen

Wenn Sie eine große Anzahl von Elementen (oder eine Liste mit unbekannter Länge) anzeigen müssen, kann die Verwendung eines Layouts wie Column zu Leistungsproblemen führen, da alle Elemente zusammengesetzt sind und unabhängig davon, ob sie sichtbar sind, dargestellt werden.

„Compose“ bietet eine Reihe von Komponenten, mit denen nur Elemente erstellt und angeordnet werden, die im Darstellungsbereich der Komponente sichtbar sind. Zu diesen Komponenten gehören LazyColumn und LazyRow.

Wie der Name schon sagt, besteht der Unterschied zwischen LazyColumn und LazyRow in der Ausrichtung, in der die Elemente angeordnet und gescrollt werden. LazyColumn erzeugt eine vertikal scrollbare Liste und LazyRow eine horizontal scrollende Liste.

Die Lazy-Komponenten unterscheiden sich von den meisten Layouts in Compose. Statt einen @Composable-Inhaltsblockparameter zu akzeptieren, sodass Apps zusammensetzbare Funktionen direkt ausgeben können, stellen die Lazy-Komponenten einen LazyListScope.()-Block bereit. Dieser LazyListScope-Block bietet eine DSL, mit der Apps den Inhalt des Artikels beschreiben können. Die Lazy-Komponente ist dann dafür verantwortlich, den Inhalt jedes Elements entsprechend dem Layout und der Scrollposition hinzuzufügen.

LazyListScope DSL

Der DSL von LazyListScope bietet eine Reihe von Funktionen zum Beschreiben von Elementen im Layout. Am einfachsten fügt item() ein einzelnes Element und items(Int) mehrere Elemente hinzu:

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")
    }
}

Außerdem gibt es eine Reihe von Erweiterungsfunktionen, mit denen sich Sammlungen von Elementen hinzufügen lassen, z. B. eine List. Mit diesen Erweiterungen können wir das obige Column-Beispiel einfach migrieren:

/**
 * import androidx.compose.foundation.lazy.items
 */
LazyColumn {
    items(messages) { message ->
        MessageRow(message)
    }
}

Es gibt auch eine Variante der Erweiterungsfunktion items() mit dem Namen itemsIndexed(), die den Index bereitstellt. Weitere Informationen finden Sie in der Referenz zu LazyListScope.

Lazy Grids

Die zusammensetzbaren Funktionen LazyVerticalGrid und LazyHorizontalGrid unterstützen die Darstellung von Elementen in einem Raster. Bei einem Lazy vertikalen Raster werden die Elemente in einem vertikal scrollbaren Container angezeigt, der sich über mehrere Spalten erstreckt. Das Lazy horizontale Raster hingegen verhält sich auf der horizontalen Achse gleich.

Raster haben die gleichen leistungsstarken API-Funktionen wie Listen und verwenden außerdem eine sehr ähnliche DSL – LazyGridScope.() zur Beschreibung des Inhalts.

Screenshot eines Smartphones mit einem Raster von Fotos

Der Parameter columns in LazyVerticalGrid und der Parameter rows in LazyHorizontalGrid steuern, wie Zellen in Spalten oder Zeilen umgewandelt werden. Im folgenden Beispiel werden Elemente in einem Raster dargestellt. Dabei wird mit GridCells.Adaptive festgelegt, dass jede Spalte mindestens 128.dp breit ist:

LazyVerticalGrid(
    columns = GridCells.Adaptive(minSize = 128.dp)
) {
    items(photos) { photo ->
        PhotoItem(photo)
    }
}

Mit LazyVerticalGrid können Sie eine Breite für Elemente angeben. Dann passt das Raster in so viele Spalten wie möglich. Die verbleibende Breite wird gleichmäßig auf die Spalten verteilt, nachdem die Anzahl der Spalten berechnet wurde. Diese adaptive Art der Größenanpassung ist besonders nützlich, um Gruppen von Elementen verschiedener Bildschirmgrößen darzustellen.

Wenn Sie die genaue Anzahl der zu verwendenden Spalten kennen, können Sie stattdessen eine Instanz von GridCells.Fixed mit der Anzahl der erforderlichen Spalten angeben.

Wenn in Ihrem Design nur bestimmte Elemente nicht standardmäßige Abmessungen haben müssen, können Sie die Rasterunterstützung verwenden, um benutzerdefinierte Spaltenspannen für Elemente anzugeben. Geben Sie den Spaltenbereich mit dem Parameter span der Methoden LazyGridScope DSL item und items an. maxLineSpan, einer der Werte des Span-Bereichs, ist besonders nützlich, wenn Sie die adaptive Größenanpassung verwenden, da die Anzahl der Spalten nicht festgelegt ist. In diesem Beispiel wird gezeigt, wie Sie einen vollständigen Zeilenbereich angeben:

LazyVerticalGrid(
    columns = GridCells.Adaptive(minSize = 30.dp)
) {
    item(span = {
        // LazyGridItemSpanScope:
        // maxLineSpan
        GridItemSpan(maxLineSpan)
    }) {
        CategoryCard("Fruits")
    }
    // ...
}

Verzögertes, gestaffeltes Raster

LazyVerticalStaggeredGrid und LazyHorizontalStaggeredGrid sind zusammensetzbare Funktionen, mit denen Sie ein Lazy-Loading-Raster von Elementen erstellen können. Bei einem vertikal gestaffelten vertikalen Raster werden die Elemente in einem vertikal scrollbaren Container angezeigt, der sich über mehrere Spalten erstreckt und einzelne Elemente unterschiedliche Höhen ermöglicht. Lazy horizontale Raster haben auf der horizontalen Achse dasselbe Verhalten bei Elementen unterschiedlicher Breite.

Das folgende Snippet ist ein einfaches Beispiel für die Verwendung von LazyVerticalStaggeredGrid mit einer Breite von 200.dp pro Element:

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()
)

Abbildung 1. Beispiel für ein verzögertes vertikales vertikales Raster

Wenn Sie eine feste Anzahl von Spalten festlegen möchten, können Sie StaggeredGridCells.Fixed(columns) anstelle von StaggeredGridCells.Adaptive verwenden. Dadurch wird die verfügbare Breite durch die Anzahl der Spalten (oder Zeilen für ein horizontales Raster) geteilt und jedes Element nimmt diese Breite (bzw. Höhe bei einem horizontalen Raster) ein:

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()
)

Verzögertes, gestaffeltes Bildraster in Compose
Abbildung 2. Beispiel für ein verzögertes vertikales vertikales Raster mit festen Spalten

Abstände im Inhalt

Manchmal müssen Sie an den Rändern des Inhalts einen Innenrand hinzufügen. Mit den verzögerten Komponenten können Sie PaddingValues an den Parameter contentPadding übergeben, um dies zu unterstützen:

LazyColumn(
    contentPadding = PaddingValues(horizontal = 16.dp, vertical = 8.dp),
) {
    // ...
}

In diesem Beispiel fügen wir für die horizontalen Ränder (links und rechts) einen Abstand von 16.dp und dann zum oberen und unteren Rand des Inhalts 8.dp hinzu.

Dieser Abstand wird auf den Inhalt und nicht auf den LazyColumn selbst angewendet. Im Beispiel oben wird für das erste Element ein Abstand von 8.dp oben hinzugefügt, beim letzten Element wird unten ein Abstand von 8.dp hinzugefügt und für alle Elemente wird links und rechts ein Abstand von 16.dp angezeigt.

Abstand zwischen Inhalten

Für zusätzlichen Abstand zwischen Elementen können Sie Arrangement.spacedBy() verwenden. Im folgenden Beispiel wird zwischen den einzelnen Elementen ein Leerzeichen (4.dp) eingefügt:

LazyColumn(
    verticalArrangement = Arrangement.spacedBy(4.dp),
) {
    // ...
}

Gleiches gilt für LazyRow:

LazyRow(
    horizontalArrangement = Arrangement.spacedBy(4.dp),
) {
    // ...
}

Raster können jedoch sowohl vertikal als auch horizontal angeordnet werden:

LazyVerticalGrid(
    columns = GridCells.Fixed(2),
    verticalArrangement = Arrangement.spacedBy(16.dp),
    horizontalArrangement = Arrangement.spacedBy(16.dp)
) {
    items(photos) { item ->
        PhotoItem(item)
    }
}

Artikelschlüssel

Standardmäßig wird der Status jedes Elements von der Position des Elements in der Liste oder dem Raster abhängig gemacht. Dies kann jedoch zu Problemen führen, wenn sich der Datensatz ändert, da Elemente, die ihre Position ändern, alle gespeicherten Elemente verlieren. Wenn Sie sich das Szenario von LazyRow innerhalb einer LazyColumn vorstellen und die Zeile die Position eines Elements ändert, würde der Nutzer seine Scrollposition innerhalb der Zeile verlieren.

Um dies zu verhindern, können Sie für jedes Element einen stabilen und eindeutigen Schlüssel und einen Block für den Parameter key angeben. Wenn Sie einen stabilen Schlüssel angeben, ist der Elementstatus bei allen Datensatzänderungen einheitlich:

LazyColumn {
    items(
        items = messages,
        key = { message ->
            // Return a stable + unique key for the item
            message.id
        }
    ) { message ->
        MessageRow(message)
    }
}

Durch die Angabe von Schlüsseln hilfst du Compose bei der korrekten Verarbeitung von Neuanordnungen. Wenn Ihr Element beispielsweise den Status „mered“ enthält, können Sie mit den Tastenkombination festlegen, dass dieser Status zusammen mit dem Element verschoben werden kann, wenn sich seine Position ändert.

LazyColumn {
    items(books, key = { it.id }) {
        val rememberedValue = remember {
            Random.nextInt()
        }
    }
}

Es gibt jedoch eine Einschränkung hinsichtlich der Typen, die Sie als Elementschlüssel verwenden können. Der Schlüsseltyp muss von Bundle unterstützt werden. Dies ist der Mechanismus von Android zum Beibehalten des Status beim Neuerstellen der Aktivität. Bundle unterstützt Typen wie Primitive, Enums oder Parcelables.

LazyColumn {
    items(books, key = {
        // primitives, enums, Parcelable, etc.
    }) {
        // ...
    }
}

Der Schlüssel muss von Bundle unterstützt werden, damit die rememberSaveable in der zusammensetzbaren Funktion des Elements wiederhergestellt werden kann, wenn die Aktivität neu erstellt wird oder wenn Sie vom Element weg scrollen und zurückscrollen.

LazyColumn {
    items(books, key = { it.id }) {
        val rememberedValue = rememberSaveable {
            Random.nextInt()
        }
    }
}

Elementanimationen

Wenn Sie das RecyclerView-Widget verwendet haben, wissen Sie, dass es Elementänderungen automatisch animiert. Lazy Layouts bieten die gleiche Funktionalität für die Neuanordnung von Elementen. Die API ist einfach. Sie müssen nur den Modifikator animateItemPlacement auf den Inhalt des Artikels festlegen:

LazyColumn {
    items(books, key = { it.id }) {
        Row(Modifier.animateItemPlacement()) {
            // ...
        }
    }
}

Sie können sogar eine Spezifikation für benutzerdefinierte Animationen angeben, wenn Folgendes erforderlich ist:

LazyColumn {
    items(books, key = { it.id }) {
        Row(
            Modifier.animateItemPlacement(
                tween(durationMillis = 250)
            )
        ) {
            // ...
        }
    }
}

Achten Sie darauf, Schlüssel für die Elemente anzugeben, damit Sie die neue Position des verschobenen Elements finden können.

Abgesehen von Neuanordnungen befinden sich derzeit Animationen zum Hinzufügen und Entfernen von Elementen in der Entwicklung. Den Fortschritt können Sie unter Ausgabe 150812265 verfolgen.

Fixierte Überschriften (experimentell)

Das Muster „fixierte Kopfzeile“ ist bei der Anzeige von Listen gruppierter Daten hilfreich. Unten sehen Sie ein Beispiel für eine Kontaktliste, die nach den Anfangsbuchstaben jedes Kontakts gruppiert ist:

Video eines Smartphones, das durch eine Kontaktliste nach oben und unten scrollt

Um mit LazyColumn einen fixierten Header zu erstellen, können Sie die experimentelle Funktion stickyHeader() verwenden und den Inhalt des Headers angeben:

@OptIn(ExperimentalFoundationApi::class)
@Composable
fun ListWithHeader(items: List<Item>) {
    LazyColumn {
        stickyHeader {
            Header()
        }

        items(items) { item ->
            ItemRow(item)
        }
    }
}

Um eine Liste mit mehreren Überschriften wie im obigen Beispiel mit der Kontaktliste zu erstellen, könnten Sie Folgendes tun:

// 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)
            }
        }
    }
}

Auf Scrollposition reagieren

Viele Apps müssen auf Änderungen an der Scrollposition und dem Layout von Elementen reagieren und darauf achten. Die Lazy-Komponenten unterstützen diesen Anwendungsfall, indem sie LazyListState hochziehen:

@Composable
fun MessageList(messages: List<Message>) {
    // Remember our own LazyListState
    val listState = rememberLazyListState()

    // Provide it to LazyColumn
    LazyColumn(state = listState) {
        // ...
    }
}

Bei einfachen Anwendungsfällen benötigen Apps normalerweise nur Informationen zum ersten sichtbaren Element. Für LazyListState werden die Attribute firstVisibleItemIndex und firstVisibleItemScrollOffset bereitgestellt.

Wenn wir das Beispiel des Anzeigen- und Ausblendens einer Schaltfläche verwenden, je nachdem, ob die Nutzenden über das erste Element hinaus gescrollt haben:

@OptIn(ExperimentalAnimationApi::class)
@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()
        }
    }
}

Es ist nützlich, den Status direkt in der Zusammensetzung zu lesen, wenn Sie andere zusammensetzbare UI-Elemente aktualisieren müssen. Es gibt aber auch Szenarien, in denen das Ereignis nicht in derselben Zusammensetzung verarbeitet werden muss. Ein gängiges Beispiel dafür ist das Senden eines Analytics-Ereignisses, sobald der Nutzer an einem bestimmten Punkt weitergescrollt hat. Um dies effizient zu verarbeiten, können wir einen snapshotFlow() verwenden:

val listState = rememberLazyListState()

LazyColumn(state = listState) {
    // ...
}

LaunchedEffect(listState) {
    snapshotFlow { listState.firstVisibleItemIndex }
        .map { index -> index > 0 }
        .distinctUntilChanged()
        .filter { it }
        .collect {
            MyAnalyticsService.sendScrolledPastFirstItemEvent()
        }
}

LazyListState stellt über das Attribut layoutInfo auch Informationen zu allen aktuell angezeigten Elementen und ihren Grenzen auf dem Bildschirm bereit. Weitere Informationen finden Sie in der Klasse LazyListLayoutInfo.

Scrollposition steuern

Es ist nicht nur sinnvoll, wenn Apps auf die Scrollposition reagieren, sondern auch die Scrollposition steuern können. LazyListState unterstützt dies über die Funktion scrollToItem(), die „sofort“ die Scrollposition anfängt, und animateScrollToItem(), die durch eine Animation scrollt (auch als flüssiges Scrollen bezeichnet):

@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)
            }
        }
    )
}

Große Datensätze (Pging)

Mithilfe der Paging-Bibliothek können Apps umfangreiche Listen von Elementen unterstützen und kleine Listenabschnitte nach Bedarf laden und anzeigen. Ab Version 3.0 wird Compose über die androidx.paging:paging-compose-Bibliothek unterstützt.

Um eine Liste der Seiteninhalte anzuzeigen, können wir die Erweiterungsfunktion collectAsLazyPagingItems() verwenden und dann den zurückgegebenen LazyPagingItems an items() in unserem LazyColumn übergeben. Ähnlich wie Paging-Unterstützung in Ansichten können Sie Platzhalter einblenden, während Daten geladen werden. Prüfen Sie dazu, ob item auf null gesetzt ist:

@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()
            }
        }
    }
}

Tipps zur Verwendung von Lazy-Layouts

Es gibt einige Tipps, die Sie berücksichtigen können, damit Ihre Lazy-Layouts wie vorgesehen funktionieren.

Verwenden Sie keine Elemente mit einer Größe von 0 Pixeln.

Dies kann in Szenarien vorkommen, in denen Sie beispielsweise davon ausgehen, einige Daten wie Bilder asynchron abzurufen, um die Listenelemente später zu füllen. Dies würde dazu führen, dass das Lazy-Layout alle Elemente in der ersten Messung zusammensetzt, da ihre Höhe 0 Pixel beträgt und alle in den Darstellungsbereich passen könnten. Sobald die Elemente geladen und ihre Höhe maximiert wurden, werden in Lazy-Layouts alle anderen Elemente verworfen, die beim ersten Mal unnötigerweise zusammengesetzt wurden, da sie nicht in den Darstellungsbereich passen. Um dies zu vermeiden, sollten Sie eine Standardgrößenanpassung für Ihre Elemente festlegen, damit das Lazy-Layout korrekt berechnen kann, wie viele Elemente tatsächlich in den Darstellungsbereich passen:

@Composable
fun Item(imageUrl: String) {
    AsyncImage(
        model = rememberAsyncImagePainter(model = imageUrl),
        modifier = Modifier.size(30.dp),
        contentDescription = null
        // ...
    )
}

Wenn Sie die ungefähre Größe Ihrer Elemente kennen, nachdem die Daten asynchron geladen wurden, sollten Sie darauf achten, dass die Größe der Elemente vor und nach dem Laden gleich bleibt. Fügen Sie beispielsweise einige Platzhalter hinzu. Dies trägt dazu bei, die korrekte Scrollposition beizubehalten.

Verschachteln Sie keine Komponenten, die in dieselbe Richtung gescrollt werden können.

Dies gilt nur, wenn scrollbare untergeordnete Elemente ohne eine vordefinierte Größe in einem anderen scrollbaren übergeordneten Element derselben Richtung verschachteln werden. Beispiel: Es wird versucht, ein untergeordnetes LazyColumn ohne feste Höhe in einem vertikal scrollbaren übergeordneten Column-Element zu verschachteln:

// throws IllegalStateException
Column(
    modifier = Modifier.verticalScroll(state)
) {
    LazyColumn {
        // ...
    }
}

Stattdessen können Sie dasselbe Ergebnis erzielen, indem Sie alle zusammensetzbaren Funktionen in eine übergeordnete LazyColumn zusammenfassen und deren DSL verwenden, um verschiedene Inhaltstypen zu übergeben. Dadurch können sowohl einzelne Elemente als auch mehrere Listenelemente an einem Ort ausgegeben werden:

LazyColumn {
    item {
        Header()
    }
    items(data) { item ->
        PhotoItem(item)
    }
    item {
        Footer()
    }
}

Zulässig sind Fälle, in denen verschiedene Richtungslayouts verschachteln sind, z. B. ein scrollbarer übergeordneter Row und ein untergeordneter LazyColumn:

Row(
    modifier = Modifier.horizontalScroll(scrollState)
) {
    LazyColumn {
        // ...
    }
}

Das gilt auch für Fälle, in denen Sie weiterhin Layouts mit derselben Richtung verwenden, aber auch eine feste Größe für die verschachtelten untergeordneten Elemente festlegen:

Column(
    modifier = Modifier.verticalScroll(scrollState)
) {
    LazyColumn(
        modifier = Modifier.height(200.dp)
    ) {
        // ...
    }
}

Vorsicht, mehrere Elemente in einem Element zu platzieren

In diesem Beispiel gibt das zweite Lambda-Element zwei Elemente in einem Block aus:

LazyVerticalGrid(
    columns = GridCells.Adaptive(100.dp)
) {
    item { Item(0) }
    item {
        Item(1)
        Item(2)
    }
    item { Item(3) }
    // ...
}

Lazy Layouts behandeln dies wie erwartet – sie legen Elemente nacheinander an, als wären sie verschiedene Elemente. Dabei gibt es jedoch ein paar Probleme.

Wenn mehrere Elemente als Teil eines Elements ausgegeben werden, werden sie als eine Entität behandelt, was bedeutet, dass sie nicht mehr einzeln zusammengestellt werden können. Wenn ein Element auf dem Bildschirm sichtbar wird, müssen alle Elemente, die dem Element entsprechen, zusammengesetzt und gemessen werden. Übermäßige Nutzung kann sich negativ auf die Leistung auswirken. Im Extremfall, dass alle Elemente in einem Element enthalten sind, widerspricht dies dem Zweck der Verwendung von Lazy-Layouts. Abgesehen von möglichen Leistungsproblemen kann es passieren, dass scrollToItem() und animateScrollToItem() beeinträchtigt werden, wenn mehr Elemente in einem Element enthalten sind.

Es gibt jedoch sinnvolle Anwendungsfälle für die Platzierung mehrerer Elemente in einem Element, z. B. Trennlinien in einer Liste. Trennlinien sollten die Scroll-Indexe nicht ändern, da sie nicht als unabhängige Elemente betrachtet werden sollten. Außerdem wird die Leistung nicht beeinträchtigt, da die Trennlinien klein sind. Eine Trennlinie muss wahrscheinlich sichtbar sein, wenn das Element davor sichtbar ist, damit sie Teil des vorherigen Elements sein können:

LazyVerticalGrid(
    columns = GridCells.Adaptive(100.dp)
) {
    item { Item(0) }
    item {
        Item(1)
        Divider()
    }
    item { Item(2) }
    // ...
}

Individuelle Anordnungen in Betracht ziehen

In der Regel enthalten Lazy-Listen viele Elemente und belegen mehr als die Größe des scrollbaren Containers. Wenn Ihre Liste jedoch nur wenige Elemente enthält, können für Ihr Design spezifischere Anforderungen an die Positionierung dieser Elemente im Darstellungsbereich gelten.

Dazu können Sie die benutzerdefinierte Branche Arrangement verwenden und an LazyColumn übergeben. Im folgenden Beispiel muss für das TopWithFooter-Objekt nur die Methode arrange implementiert werden. Erstens werden die Elemente nacheinander platziert. Wenn die insgesamt verwendete Höhe niedriger ist als die Höhe des Darstellungsbereichs, wird die Fußzeile unten platziert:

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()
        }
    }
}

Du kannst „contentType“ hinzufügen

Ab Compose 1.2 sollten Sie contentType in Ihre Listen oder Raster aufnehmen, um die Leistung des Lazy-Layouts zu maximieren. So können Sie den Inhaltstyp für jedes Element des Layouts angeben, wenn Sie eine Liste oder ein Raster aus mehreren verschiedenen Elementtypen erstellen:

LazyColumn {
    items(elements, contentType = { it.type }) {
        // ...
    }
}

Wenn Sie die contentType angeben, kann Composer nur Kompositionen zwischen Elementen desselben Typs wiederverwenden. Da die Wiederverwendung effizienter ist, wenn Sie Elemente mit ähnlicher Struktur zusammensetzen, wird durch die Angabe der Inhaltstypen sichergestellt, dass Composer nicht versucht, ein Element des Typs A zusätzlich zu einem völlig anderen Element des Typs B zusammenzustellen. So können Sie die Vorteile der Wiederverwendung von Kompositionen und die Leistung des Lazy-Layouts maximieren.

Leistungsmessung

Sie können die Leistung eines Lazy-Layouts nur zuverlässig messen, wenn Sie im Release-Modus ausgeführt werden und die R8-Optimierung aktiviert ist. Bei Debug-Builds kann Lazy Layout Scrolling langsamer sein. Weitere Informationen dazu finden Sie unter Compose-Leistung.