Listen und Raster

In vielen Apps müssen Sammlungen von Elementen angezeigt werden. In diesem Dokument wird erläutert, wie Sie ist das mit Jetpack Compose effizient möglich.

Wenn Sie wissen, dass für Ihren Anwendungsfall kein Scrollen erforderlich ist, Verwenden Sie ein einfaches Column oder Row (je nach Richtung) und geben Sie den Inhalt jedes Elements aus, indem Sie wie folgt über eine Liste iterieren:

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

Mit dem verticalScroll()-Modifikator können wir Column scrollbar machen.

Lazy Listen

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

Compose bietet eine Reihe von Komponenten, die nur Elemente zusammenstellen und layouten, die im Darstellungsbereich der Komponente sichtbar sind. Zu diesen Komponenten gehören: LazyColumn und LazyRow

Wie der Name schon sagt, ist der Unterschied zwischen LazyColumn und LazyRow ist die Ausrichtung, in der sie ihre Objekte anordnen und scrollen. Mit LazyColumn wird eine vertikal scrollbare Liste und mit LazyRow eine horizontal scrollbare Liste erzeugt.

Die Lazy-Komponenten unterscheiden sich von den meisten Layouts in Compose. Anstatt einen @Composable-Inhaltsblockparameter zu akzeptieren, mit dem Apps direkt Composables ausgeben können, bieten die Lazy-Komponenten einen LazyListScope.()-Block. Dieses LazyListScope Block bietet eine DSL, mit der Apps den Inhalt des Artikels beschreiben können. Die Die Lazy-Komponente ist dann dafür verantwortlich, die Inhalte der einzelnen Elemente die für das Layout und die Scroll-Position erforderlich sind.

LazyListScope DSL

Die DSL von LazyListScope bietet eine Reihe von Funktionen zum Beschreiben von Elementen im Layout. Ganz einfach: item() ein einzelnes Element hinzufügt und items(Int) fügt 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 Sammlung von Elementen, z. B. List. Mit diesen Erweiterungen können wir unser Beispiel Column oben ganz einfach migrieren:

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

Es gibt auch eine Variante der Erweiterungsfunktion items(), die itemsIndexed() genannt wird und den Index liefert. In der LazyListScope finden Sie weitere Informationen.

Lazy Grids

Die Composeables LazyVerticalGrid und LazyHorizontalGrid unterstützen die Anzeige von Elementen in einem Raster. In einem Lazy Vertical Grid werden die Elemente in einem vertikal scrollbaren Container angezeigt, der sich über mehrere Spalten erstreckt. Bei Lazy Horizontal Grids verhält es sich auf der horizontalen Achse genauso.

Raster haben dieselben leistungsstarken API-Funktionen wie Listen und verwenden auch eine sehr ähnliche DSL (LazyGridScope.()), um den Inhalt zu beschreiben.

Screenshot eines Smartphones mit einem Raster von Fotos

Der columns-Parameter in LazyVerticalGrid und rows in LazyHorizontalGrid wie Zellen in Spalten oder Zeilen umgewandelt werden. Im folgenden Beispiel werden Elemente in einem Raster angezeigt. Mit GridCells.Adaptive wird festgelegt, dass jede Spalte mindestens 128.dp breit sein muss:

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

Mit LazyVerticalGrid können Sie eine Breite für Elemente angeben. Spalten wie möglich einfügen. 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 für die Darstellung von Gruppen von Elementen. für unterschiedliche 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.

Wenn in Ihrem Design nur bestimmte Elemente nicht standardmäßige Abmessungen haben müssen, können Sie mithilfe der Rasterunterstützung benutzerdefinierte Spalten-Spans für Elemente angeben. Geben Sie die Spaltenspanne mit dem Parameter span der Methoden LazyGridScope DSLitem und items an. maxLineSpan, einer der Werte für den Bereichsumfang, ist besonders nützlich, wenn Sie die adaptive Größe 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")
    }
    // ...
}

Lazy staggered grid

LazyVerticalStaggeredGrid und LazyHorizontalStaggeredGrid sind Composeables, mit denen Sie ein Lazy-Load-Staggered-Grid mit Elementen erstellen können. In einem Lazy Vertical Staggered Grid werden die Elemente in einem vertikal scrollbaren Container angezeigt, der sich über mehrere Spalten erstreckt und es ermöglicht, dass einzelne Elemente unterschiedliche Höhen haben. Verzögerte horizontale Raster haben dasselbe Verhalten horizontale Achse mit 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 anstelle von StaggeredGridCells.Adaptive die Zahl StaggeredGridCells.Fixed(columns) verwenden. Die verfügbare Breite wird durch die Anzahl der Spalten (oder Zeilen für eine horizontales Raster) und jedes Element nimmt diese Breite (bzw. Höhe horizontales Raster):

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 Lazy-Staggered-vertikales Raster mit festen Spalten

Textabstand

Manchmal müssen Sie an den Rändern des Inhalts einen Innenrand hinzufügen. Mit den Lazy-Komponenten kannst du dem Parameter contentPadding einige PaddingValues übergeben, um dies zu unterstützen:

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

In diesem Beispiel fügen wir den horizontalen Rändern (links und rechts) einen Abstand von 16.dp und dann oben und unten am Inhalt einen Abstand von 8.dp hinzu.

Dieser Abstand wird auf den content angewendet, nicht auf den LazyColumn selbst. Im Beispiel oben wird dem ersten Element oben ein Abstand von 8.dp hinzugefügt, dem letzten Element unten 8.dp und allen Elementen links und rechts 16.dp.

Abstand zwischen Inhalten

Mit Arrangement.spacedBy() können Sie Abstände zwischen Elementen hinzufügen. 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 anhand der Position des Elements in der Liste oder im Raster festgelegt. Dies kann jedoch zu Problemen führen, wenn sich der Datensatz ändert, da Elemente, die dass die Positionsänderung faktisch alle gespeicherten Informationen verliert. Stellen Sie sich das Szenario von LazyRow in einer LazyColumn vor. Wenn sich die Artikelposition in der Zeile ändert, verliert der Nutzer seine Scrollposition innerhalb der Zeile.

Um dies zu verhindern, können Sie für jedes Element einen stabilen und eindeutigen Schlüssel bereitstellen, einen Block zum Parameter key hinzu. Wenn Sie einen stabilen Schlüssel bereitstellen, kann der Artikelstatus bei allen Dataset-Änderungen einheitlich sind:

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 einen gespeicherten Status enthält, können Sie mithilfe von Schlüsseln festlegen, dass Compose diesen Status zusammen mit dem Element verschiebt, 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, dem Android-Mechanismus zum Beibehalten der Status, wenn die Aktivität neu erstellt 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, sodass die rememberSaveable darin kann das zusammensetzbare Element wiederhergestellt werden, wenn die Activity-Klasse neu erstellt wird. wenn Sie von diesem Element weg 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 damit Elementänderungen automatisch animiert werden. Lazy-Layouts bieten dieselben Funktionen für die Artikelneuanordnung. Die API ist einfach: Du musst nur den Modifikator animateItemPlacement für den Artikelinhalt festlegen:

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()) {
            // ...
        }
    }
}

Sie können sogar eine benutzerdefinierte Animationsspezifikation angeben, wenn Folgendes erforderlich ist:

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

Stellen Sie sicher, dass Sie Schlüssel für Ihre Elemente angeben, damit Sie neue Position des verschobenen Elements an.

Neben der Neuanordnung werden derzeit Artikelanimationen für Hinzufügungen und Entfernungen entwickelt. Sie können den Fortschritt unter Issue 150812265 verfolgen.

Fixierte Überschriften (experimentell)

Das Muster „Fixierte Überschrift“ ist hilfreich, wenn Listen mit gruppierten Daten angezeigt werden. Unten sehen Sie ein Beispiel für eine Kontaktliste, die nach den Anfang:

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

Wenn Sie mit LazyColumn eine fixierte Überschrift erstellen möchten, können Sie die experimentelle Funktion stickyHeader() verwenden und den Inhalt der Überschrift angeben:

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

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

Um eine Liste mit mehreren Überschriften zu erstellen, wie im Beispiel mit der Kontaktliste oben, 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 die 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 die LazyListState hochskalieren:

@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 benötigen Apps meist nur Informationen über die erste sichtbares Element. Dazu bietet LazyListState die Properties firstVisibleItemIndex und firstVisibleItemScrollOffset.

Angenommen, Sie möchten eine Schaltfläche je nachdem anzeigen oder ausblenden, ob der Nutzer zum ersten Element gescrollt hat:

@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 eine Aktualisierung vornehmen müssen andere zusammensetzbare Funktionen der Benutzeroberfläche. Es gibt aber auch Szenarien, in denen das Ereignis in derselben Komposition behandelt werden. Ein gängiges Beispiel dafür ist das Senden einer wenn der Nutzer an einem bestimmten Punkt weiterscrollt. Um das Problem zu beheben können wir eine snapshotFlow():

val listState = rememberLazyListState()

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

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

LazyListState bietet über die Property layoutInfo auch Informationen zu allen derzeit angezeigten Elementen und ihren Begrenzungen auf dem Bildschirm. Weitere Informationen finden Sie im Artikel zur Klasse LazyListLayoutInfo.

Scrollposition steuern

Es ist nicht nur nützlich, wenn Apps auf die Scrollposition reagieren, auch die Scroll-Position. LazyListState unterstützt dies über die Funktion scrollToItem(), mit der die Scrollposition „sofort“ fixiert wird, und animateScrollToItem(), mit der per Animation (auch als „weiches Scrollen“ bezeichnet) gescrollt wird:

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

Mit der Paging Library können Apps lange Listen von Elementen unterstützen, kleine Listenelemente als notwendig ist. Paging 3.0 und höher bietet Compose-Unterstützung über die androidx.paging:paging-compose-Bibliothek.

Um eine Liste von Seiteninhalten anzuzeigen, können wir die Methode collectAsLazyPagingItems() Erweiterungsfunktion und übergeben die zurückgegebene LazyPagingItems an items() in unserem LazyColumn. Ähnlich wie die Paging-Unterstützung in Ansichten können Sie Platzhalter während des Ladens der Daten anzeigen, indem Sie prüfen, 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.

Das kann in Szenarien passieren, in denen Sie beispielsweise asynchron einige Daten wie Bilder abrufen möchten, um die Elemente Ihrer Liste später zu füllen. Dies würde dazu führen, dass das Lazy-Layout alle Elemente im ersten da ihre Höhe 0 Pixel beträgt und sie alle in das Darstellungsbereich. Sobald die Elemente geladen und ihre Höhe maximiert wurden, werden Lazy-Layouts werden dann alle anderen unnötigerweise erstellten Elemente verworfen. da sie nicht in den Darstellungsbereich passen. Um dies zu vermeiden, sollten Sie für Ihre Elemente eine Standardgröße 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 Artikel kennen, nachdem die Daten asynchron geladen werden, sollten Sie darauf achten, dass die Größe Ihrer Artikel vor und nach dem Laden durch Hinzufügen von Platzhaltern identisch. So bleibt die richtige Scrollposition erhalten.

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

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

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

Stattdessen können Sie dasselbe Ergebnis erzielen, indem Sie alle Ihre Composeables in einem übergeordneten LazyColumn-Element einschließen und über die DSL unterschiedliche Inhaltstypen übergeben. So können sowohl einzelne als auch mehrere Listenelemente ausgegeben werden. alles an einem Ort:

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

Denken Sie daran, dass Fälle, in denen Sie verschiedene Richtungslayouts verschachteln, Beispielsweise sind ein scrollbarer übergeordneter Row und ein untergeordneter LazyColumn zulässig:

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

Außerdem gibt es Fälle, in denen Sie immer noch dieselben Richtungslayouts verwenden, aber auch eine feste Größe für die verschachtelten untergeordneten Elemente:

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 die ersten Elemente so als wären es unterschiedliche Elemente. Es gibt jedoch ein paar Probleme dabei.

Wenn mehrere Elemente als Teil eines Elements ausgegeben werden, werden sie wie d. h., sie können nicht mehr einzeln zusammengestellt werden. Wenn ein Element auf dem Bildschirm sichtbar wird, müssen alle Elemente, die dem Element entsprechen, zusammengesetzt und gemessen werden. Dies kann sich negativ auf die Leistung auswirken, wenn übermäßig oft verwendet wird. Im Extremfall, dass alle Elemente in ein Element den Zweck der Verwendung von Lazy-Layouts vollständig zunichte. Abgesehen von möglichen kann die Leistung beeinträchtigt werden, mit scrollToItem() und animateScrollToItem().

Es gibt jedoch durchaus Anwendungsfälle, in denen mehrere Elemente in einem Element sinnvoll sind, z. B. Trennlinien in einer Liste. Sie sollten nicht möchten, dass Trennlinien die Scrollindexe ändern, da sie nicht als eigenständige Elemente betrachtet werden sollten. Außerdem wirkt sich die Verwendung von Trennlinien nicht auf die Leistung aus, da sie klein sind. Eine Trennlinie muss wahrscheinlich sichtbar, wenn das Element davor sichtbar ist, sodass es Teil des vorherigen Artikel:

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

Benutzerdefinierte Arrangements verwenden

In der Regel enthalten Lazy-Listen viele Elemente und nehmen mehr Platz ein als der scrollbare Container. Wenn Ihre Liste jedoch nur wenige Einträge enthält, Design kann spezifischere Anforderungen an die Positionierung dieser Elemente haben. im Darstellungsbereich befinden.

Dazu können Sie benutzerdefinierte Branchen Arrangement und an LazyColumn übergeben. Im folgenden Beispiel wird der TopWithFooter muss nur die arrange-Methode implementiert werden. Erstens: Elemente nacheinander anzeigen. Wenn die insgesamt verwendete Höhe niedriger ist als die Höhe des Darstellungsbereichs befindet sich die Fußzeile unten:

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

Fügen Sie contentType hinzu

Ab Compose 1.2 können Sie die Leistung Ihres Lazy-Layouts maximieren, indem Sie Ihren Listen oder Rastern contentType hinzufügen. Damit können Sie den Inhaltstyp für jede des Layouts erstellen, wenn Sie eine Liste oder ein Raster aus von verschiedenen Arten von Elementen:

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

Wenn Sie die contentType, Mit „Compose“ können nur Kompositionen wiederverwendet werden zwischen den Elementen desselben Typs. Da die Wiederverwendung effizienter ist, Elemente ähnlicher Struktur zusammensetzen. Durch die Bereitstellung der Inhaltstypen Beim Schreiben wird nicht versucht, ein Element des Typs A über einem ein anderes Element des Typs B. So lassen sich 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 Release-Modus und mit aktivierter R8-Optimierung. Bei Debug-Builds: Lazy Layout kann das Scrollen langsamer erscheinen. Weitere Informationen finden Sie unter Leistung von Kompositionen.