Listen und Raster

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

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

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

Column lässt sich mit dem verticalScroll()-Modifikator scrollbar machen. Weitere Informationen finden Sie in der Dokumentation zu Gesten.

Verzögerte 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 und angeordnet werden, unabhängig davon, ob sie sichtbar sind oder nicht.

Die Funktion „Compose“ umfasst eine Reihe von Komponenten, mit denen nur Elemente zusammengesetzt 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 sie ihre Elemente anordnen und scrollen. LazyColumn generiert eine vertikal scrollbare Liste und LazyRow eine horizontal scrollbare Liste.

Die Lazy-Komponenten unterscheiden sich von den meisten Layouts in der Funktion „Compose“. Anstelle eines @Composable-Inhaltsblockparameters, der es Apps ermöglicht, zusammensetzbare Funktionen direkt auszugeben, bieten die Lazy-Komponenten einen LazyListScope.()-Block. Dieser LazyListScope-Block bietet eine DSL, mit der Apps den Inhalt der Elemente beschreiben können. Die Lazy-Komponente ist dann dafür verantwortlich, den Inhalt der einzelnen Elemente entsprechend dem Layout und der Scrollposition hinzuzufügen.

LazyListScope DSL

Die DSL von LazyListScope bietet eine Reihe von Funktionen zum Beschreiben von Elementen im Layout. Im einfachsten Fall 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")
    }
}

Es gibt auch eine Reihe von Erweiterungsfunktionen, mit denen Sie Sammlungen von Elementen hinzufügen können, z. B. eine List. Mit diesen Erweiterungen lässt sich das obige Column-Beispiel ganz einfach migrieren:

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

Es gibt auch eine Variante der Erweiterungsfunktion items() namens 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 Anzeige von Elementen in einem Raster. Bei einem Lazy-Vertical-Raster werden die Elemente in einem vertikal scrollbaren Container angezeigt, der sich über mehrere Spalten erstreckt. Lazy horizontale Raster verhalten sich auf der horizontalen Achse dagegen 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 Fotoraster

Mit den Parametern columns in LazyVerticalGrid und dem Parameter rows in LazyHorizontalGrid wird gesteuert, wie Zellen in Spalten oder Zeilen gebildet werden. Im folgenden Beispiel werden Elemente in einem Raster dargestellt. Dabei wird mit GridCells.Adaptive für jede Spalte mindestens eine Breite von 128.dp festgelegt:

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

Mit LazyVerticalGrid können Sie eine Breite für Elemente angeben. Anschließend passt das Raster so viele Spalten wie möglich an. Die verbleibende Breite wird gleichmäßig auf die Spalten verteilt, nachdem die Anzahl der Spalten berechnet wurde. Diese adaptive Größenanpassung eignet sich besonders für die Anzeige von Elementen auf verschiedenen Bildschirmgrößen.

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 für Ihr Design nur bestimmte Elemente nicht standardmäßige Abmessungen benötigen, können Sie die Rasterunterstützung nutzen, um benutzerdefinierte Spaltenspannen für Elemente bereitzustellen. Geben Sie den Spaltenbereich mit dem Parameter span der Methoden LazyGridScope DSL item und items an. maxLineSpan, einer der Werte des Span-Bereichs, ist bei Verwendung der adaptiven Größe besonders nützlich, 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 Raster

LazyVerticalStaggeredGrid und LazyHorizontalStaggeredGrid sind zusammensetzbare Funktionen, mit denen Sie ein Lazy-Loading-Raster von Elementen erstellen können. Bei einem lazy vertikalen, gestaffelten Raster werden die Elemente in einem vertikal scrollbaren Container angezeigt, der sich über mehrere Spalten erstreckt und verschiedene Höhen einzelner Elemente ermöglicht. Verzögerte horizontale Raster verhalten sich auf der horizontalen Achse gleich, wenn Elemente unterschiedlich breit sind.

Das folgende Snippet ist ein einfaches Beispiel für die Verwendung von LazyVerticalStaggeredGrid mit einer 200.dp-Breite 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 vertikal angeordnetes vertikales Raster

Wenn Sie eine feste Anzahl von Spalten festlegen möchten, können Sie StaggeredGridCells.Fixed(columns) anstelle von StaggeredGridCells.Adaptive verwenden. Die verfügbare Breite wird durch die Anzahl der Spalten (oder Zeilen bei einem horizontalen Raster) dividiert 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()
)

Versetztes Bildraster in der Funktion „Compose“
Abbildung 2. Beispiel für ein vertikal angeordnetes vertikales Raster mit festen Spalten

Abstände zwischen Inhalten

Manchmal müssen Sie an den Rändern des Inhalts einen Innenrand hinzufügen. Mit den verzögerten Komponenten können Sie einige 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 den horizontalen Kanten (links und rechts) einen Innenabstand von 16.dp und dann 8.dp am oberen und unteren Rand des Inhalts hinzu.

Dieser Abstand wird auf den Inhalt angewendet, nicht auf den LazyColumn selbst. Im Beispiel oben fügt das erste Element am oberen Rand einen Abstand von 8.dp hinzu. Das letzte Element fügt am unteren Rand 8.dp hinzu. Alle Elemente haben links und rechts einen Abstand von 16.dp.

Abstand zwischen Inhalten

Mit Arrangement.spacedBy() können Sie den Abstand zwischen Elementen vergrößern. Im folgenden Beispiel wird zwischen den einzelnen Elementen ein Leerzeichen von 4.dp hinzugefügt:

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

Ähnliches gilt für LazyRow:

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

Raster unterstützen jedoch sowohl vertikale als auch horizontale Anordnungen:

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

Elementschlüssel

Standardmäßig ist der Status jedes Elements von der Position des Elements in der Liste oder dem Raster abhängig. Dies kann jedoch zu Problemen führen, wenn sich der Datensatz ändert, da Elemente, die sich an ihrer Position ändern, effektiv ihren gespeicherten Status verlieren. Wenn Sie sich das Szenario von LazyRow innerhalb einer LazyColumn vorstellen und die Zeile die Position des 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 angeben und einen Block für den Parameter key bereitstellen. Wenn Sie einen stabilen Schlüssel angeben, ist der Elementstatus bei allen Datensatzänderungen konsistent:

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 kann Compose Neuanordnungen richtig verarbeiten. Wenn Ihr Element beispielsweise einen gespeicherten Status enthält, können die Schlüssel festgelegt werden, dass Composer diesen Status zusammen mit dem Element verschieben 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 Typ des Schlüssels muss von Bundle unterstützt werden, dem Mechanismus von Android, mit dem der Status beim Neuerstellen der Aktivität beibehalten wird. 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 rememberSaveable innerhalb der zusammensetzbaren Funktion wiederhergestellt werden kann, wenn die Aktivität neu erstellt wird oder wenn Sie von diesem Element weg scrollen und zurückscrollen.

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

Artikelanimationen

Wenn Sie das RecyclerView-Widget verwendet haben, wissen Sie, dass es Artikelä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 animateItemPlacement-Modifikator auf den Elementinhalt setzen:

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

Sie können sogar eine benutzerdefinierte Animationsspezifikation bereitstellen, wenn Sie:

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

Geben Sie Schlüssel für Ihre Elemente an, damit Sie die neue Position des verschobenen Elements finden können.

Abgesehen von Neuanordnungen befindet sich die Entwicklung derzeit an Animationen für Hinzufügungen und Entfernungen. Sie können den Fortschritt unter Problem 150812265 verfolgen.

Fixierte Überschriften (experimentell)

Das Muster für eine fixierte Kopfzeile ist hilfreich, wenn Listen gruppierter Daten angezeigt werden. Im Folgenden sehen Sie ein Beispiel für eine Kontaktliste, die nach der Anfangsbuchstaben der jeweiligen Kontakte gruppiert ist:

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

Um einen fixierten Header mit LazyColumn zu erhalten, 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 Headern zu erstellen, wie im Beispiel für die Kontaktliste oben, können 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 Scrollpositionen und Layoutänderungen von Elementen reagieren und darauf reagieren. Die Lazy-Komponenten unterstützen diesen Anwendungsfall durch das Hochziehen der LazyListState:

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

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

Für einfache Anwendungsfälle müssen Anwendungen in der Regel nur Informationen über das erste sichtbare Element kennen. Dazu stellt LazyListState die Attribute firstVisibleItemIndex und firstVisibleItemScrollOffset bereit.

Nehmen wir als Beispiel das Anzeigen und Ausblenden einer Schaltfläche, je nachdem, ob der Nutzer am ersten Element vorbeigescrollt hat:

@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 Beispiel dafür ist das Senden eines Analyseereignisses, wenn der Nutzer an einem bestimmten Punkt vorbeigescrollt hat. Um dies effizient zu handhaben, 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 außerdem über die Property layoutInfo Informationen zu allen derzeit angezeigten Elementen und ihren Grenzen auf dem Bildschirm bereit. Weitere Informationen finden Sie in der Klasse LazyListLayoutInfo.

Scrollposition festlegen

Neben der Reaktion auf die Scrollposition ist es auch nützlich, dass Apps die Scrollposition steuern können. LazyListState unterstützt dies über die Funktion scrollToItem(), die „sofort“ die Scrollposition anklickt, und animateScrollToItem(), die mithilfe einer Animation (auch als Smooth Scrollen bezeichnet) scrollt:

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

Mit der Paging-Bibliothek können Anwendungen umfangreiche Listen von Elementen unterstützen und kleine Listenelemente nach Bedarf laden und anzeigen. Paging 3.0 und höher bieten Unterstützung für das Verfassen über die androidx.paging:paging-compose-Bibliothek.

Zum Anzeigen einer Liste mit Seiteninhalten können Sie die Erweiterungsfunktion collectAsLazyPagingItems() verwenden und dann das zurückgegebene LazyPagingItems-Element an items() in der LazyColumn übergeben. Ähnlich wie die Paginierungsunterstützung in Ansichten können Sie Platzhalter anzeigen lassen, 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 beachten können, damit Ihre Lazy-Layouts wie vorgesehen funktionieren.

Verwenden Sie keine Elemente mit der Größe 0 Pixel.

Dies kann z. B. der Fall sein, wenn Sie beispielsweise erwarten, einige Daten wie Bilder asynchron abzurufen, um die Elemente Ihrer Liste zu einem späteren Zeitpunkt zu füllen. Das würde dazu führen, dass beim Lazy-Layout alle Elemente in der ersten Messung zusammengefügt werden, da ihre Höhe 0 Pixel beträgt und sie alle in den Darstellungsbereich passen könnten. Sobald die Elemente geladen und ihre Höhe erweitert wurde, würden Lazy-Layouts alle anderen Elemente verwerfen, die beim ersten Mal unnötigerweise zusammengestellt wurden, da sie nicht in den Darstellungsbereich passen. Um dies zu vermeiden, sollten Sie die Standardgröße für Ihre Elemente festlegen, damit mit dem Lazy-Layout richtig berechnet werden kann, wie viele Elemente 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, empfiehlt es sich, dafür zu sorgen, dass die Größe der Elemente vor und nach dem Laden gleich bleibt. Fügen Sie beispielsweise Platzhalter hinzu. Dies trägt dazu bei, die richtige Scrollposition beizubehalten.

Vermeiden Sie es, Komponenten zu verschachteln, die in die gleiche Richtung gescrollt werden können.

Das gilt nur für Fälle, in denen scrollbare untergeordnete Elemente ohne vordefinierte Größe innerhalb eines übergeordneten Elements in derselben Richtung verschachtelt werden. Beispiel: Sie versuchen, ein untergeordnetes LazyColumn-Element ohne feste Höhe in einem vertikal scrollbaren Column-Element zu verschachteln:

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

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

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

In Fällen, in denen Sie unterschiedliche Richtungslayouts verschachteln, z. B. ein scrollbares übergeordnetes Row und ein untergeordnetes LazyColumn, sind zulässig:

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

Das gilt auch für Fälle, in denen Sie weiterhin dieselben Richtungslayouts 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)
    ) {
        // ...
    }
}

Vermeiden Sie es, mehrere Elemente für einen Artikel zu verwenden.

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

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

Bei Lazy-Layouts wird dies wie erwartet gehandhabt – sie ordnen die Elemente nacheinander an, als wären es verschiedene Elemente. Dabei gibt es jedoch ein paar Probleme.

Wenn mehrere Elemente als Teil eines Elements ausgegeben werden, werden sie als eine Einheit behandelt, was bedeutet, dass sie nicht mehr einzeln zusammengesetzt werden können. Wenn ein Element auf dem Bildschirm sichtbar wird, müssen alle Elemente, die diesem Element entsprechen, zusammengesetzt und gemessen werden. Dies kann bei übermäßiger Verwendung die Leistung beeinträchtigen. Im Extremfall, bei dem alle Elemente in einem Element enthalten sind, verfehlt es den Zweck von Lazy-Layouts. Wenn mehr Elemente in einem Element enthalten sind, beeinträchtigt das nicht nur die potenziellen Leistungsprobleme, sondern auch scrollToItem() und animateScrollToItem().

Es gibt jedoch zulässige Anwendungsfälle für die Aufnahme mehrerer Elemente in ein Element, z. B. Trennlinien innerhalb einer Liste. Durch Trennlinien werden Scroll-Indizes nicht verändert, denn sie sollten nicht als unabhängige Elemente Außerdem wird die Leistung nicht beeinträchtigt, da die Trennlinien klein sind. Eine Trennlinie muss wahrscheinlich sichtbar sein, wenn das Element vorher sichtbar ist, damit sie Teil des vorherigen Elements sein kann:

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

Benutzerdefinierte Anordnungen in Betracht ziehen

In der Regel umfassen Lazy-Listen viele Elemente und belegen mehr als die Größe des Scroll-Containers. Wenn Ihre Liste jedoch nur wenige Elemente enthält, kann Ihr Design spezifischere Anforderungen an die Positionierung dieser Elemente im Darstellungsbereich stellen.

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 Elemente nacheinander platziert. Wenn außerdem die gesamte verwendete Höhe niedriger als die Höhe des Darstellungsbereichs ist, wird die Fußzeile am unteren Rand 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()
        }
    }
}

Wir empfehlen, contentType hinzuzufügen

Ab Compose 1.2 können Sie contentType in Listen oder Raster aufnehmen, um die Leistung Ihres Lazy-Layouts zu maximieren. Auf diese Weise können Sie den Inhaltstyp für jedes Element des Layouts angeben, wenn Sie eine Liste oder ein Raster zusammenstellen, das aus mehreren verschiedenen Arten von Elementen besteht:

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

Wenn Sie contentType angeben, kann Composer nur Kompositionen zwischen Elementen desselben Typs wiederverwenden. Da Wiederverwendung effizienter ist, wenn Sie Elemente mit ähnlicher Struktur erstellen, wird durch Angabe der Inhaltstypen sichergestellt, dass in Composer nicht versucht, ein Element vom Typ A über ein komplett anderes Element von Typ B zu erstellen. So können Sie die Vorteile der Wiederverwendung von Kompositionen und die Leistung des Lazy-Layouts optimal nutzen.

Leistungsmessung

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