Listy i siatki

Wiele aplikacji musi wyświetlać kolekcje elementów. Z tego dokumentu dowiesz się, jak sprawnie wykonywać te czynności w Jetpack Compose.

Jeśli wiesz, że w Twoim przypadku nie jest wymagane przewijanie, możesz użyć prostego tagu Column lub Row (w zależności od kierunku) i przesyłać zawartość każdego elementu, iterując listę w taki sposób:

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

Aby element Column można było przewijać, użyj modyfikatora verticalScroll().

Leniwe listy

Jeśli musisz wyświetlić dużą liczbę elementów (lub listę o nieznanej długości), używanie układu takiego jak Column może spowodować problemy z wydajnością, ponieważ wszystkie elementy zostaną skomponowane i ułożone, niezależnie od tego, czy są widoczne.

Compose udostępnia zestaw komponentów, które tylko tworzą i umieszcza elementy, które są widoczne w widocznym obszarze komponentu. Są to między innymi LazyColumn i LazyRow.

Jak sugeruje nazwa, różnica między wersjami LazyColumn i LazyRow polega na orientacji, w jakiej są one wyświetlane i przewijane. Funkcja LazyColumn tworzy listę przewijaną w pionie, a LazyRow tworzy listę przewijaną w poziomie.

Komponenty leniwie wczytywane różnią się od większości układów w Compose. Zamiast akceptować parametr bloku treści @Composable, który umożliwia aplikacjom bezpośrednie wysyłanie elementów kompozycyjnych, komponenty Lazy mają blok LazyListScope.(). Blok LazyListScope oferuje dostęp do DSL, który umożliwia aplikacjom opisywanie zawartości elementu. Komponent leniwy odpowiada za dodawanie treści każdego elementu zgodnie z wymaganiami układu i pozycją przewijania.

LazyListScope DSL

DSL elementu LazyListScope udostępnia kilka funkcji do opisywania elementów w układzie. Ogólnie rzecz biorąc, item() dodaje jeden element, a items(Int) dodaje wiele elementów:

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

Istnieje też kilka funkcji rozszerzeń, które umożliwiają dodawanie kolekcji elementów, np. List. Dzięki tym rozszerzeniom możemy łatwo przenieść nasz przykład Column z powyżej:

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

Istnieje też wariant funkcji rozszerzenia items() o nazwie itemsIndexed(), która zwraca indeks. Więcej informacji znajdziesz w pliku referencyjnym LazyListScope.

Leniwe siatki

Komponenty LazyVerticalGrid i LazyHorizontalGridumożliwiają wyświetlanie elementów w siatce. Pionowa siatka Lazy wyświetla elementy w kontenerze, który można przewijać w pionie, obejmując wiele kolumn, a pozioma siatka Lazy zachowuje się tak samo na osi poziomej.

Sieci mają te same zaawansowane funkcje interfejsu API co listy i także używają bardzo podobnego języka DSL (LazyGridScope.()) do opisywania treści.

Zrzut ekranu z telefonu pokazujący siatkę zdjęć

Parametr columnsLazyVerticalGrid i parametr rowsLazyHorizontalGrid określają, jak komórki są formowane w kolumny lub wiersze. W tym przykładzie elementy są wyświetlane w siatce, a kolumny mają szerokość co najmniej GridCells.Adaptive:

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

LazyVerticalGrid umożliwia określenie szerokości elementów, a siatka będzie pasowała do jak największej liczby kolumn. Po obliczeniu liczby kolumn pozostała szerokość jest rozdzielana równomiernie między kolumny. Ten adaptacyjny sposób dostosowywania rozmiaru jest szczególnie przydatny w przypadku wyświetlania zestawów elementów na różnych rozmiarach ekranu.

Jeśli znasz dokładną liczbę kolumn, które mają być użyte, możesz zamiast tego podać instancję parametru GridCells.Fixed zawierającego liczbę wymaganych kolumn.

Jeśli Twój projekt wymaga, aby tylko niektóre elementy miały niestandardowe wymiary, możesz użyć funkcji siatki, by podać niestandardowe rozpiętości kolumn. Określ zakres kolumny za pomocą parametru span metod LazyGridScope DSL itemitems. maxLineSpan, jedna z wartości zakresu zasięgu, jest szczególnie przydatna, gdy używasz dostosowania rozmiaru, ponieważ liczba kolumn nie jest stała. Ten przykład pokazuje, jak podać pełny rozpiętość wierszy:

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

Leniwa rozłożona siatka

LazyVerticalStaggeredGrid i LazyHorizontalStaggeredGrid to komponenty, które umożliwiają tworzenie wczytywanych z opóźnieniem siatek elementów z przesunięciem. Leniwa siatka z rozłożeniem w pionie wyświetla elementy w kontenerze z możliwością przewijania w pionie, który obejmuje wiele kolumn i umożliwia ustawienie różnych wysokości poszczególnych elementów. Lazy horizontal grids zachowują się tak samo na osi poziomej w przypadku elementów o różnej szerokości.

Ten fragment kodu to podstawowy przykład użycia obiektu LazyVerticalStaggeredGrid z szerokością 200.dp na 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()
)

Rysunek 1. Przykład leniwej, rozłożonej w pionie siatki

Aby ustawić stałą liczbę kolumn, możesz użyć StaggeredGridCells.Fixed(columns) zamiast StaggeredGridCells.Adaptive. Dostępna szerokość jest dzielona przez liczbę kolumn (lub wierszy w przypadku siatki poziomej) i każdy element ma taką szerokość (czyli wysokość w przypadku siatki poziomej):

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

Siatka obrazów w trybie tworzenia z opóźnionym wczytywaniem
Rysunek 2. Przykład leniwej, rozłożonej pionowej siatki ze stałymi kolumnami

wypełnienie treści;

Czasami przy krawędziach zawartości trzeba dodać dopełnienie. Leniwe komponenty umożliwiają przekazywanie pewnej wartości PaddingValues do parametru contentPadding, aby to umożliwiały:

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

W tym przykładzie dodajemy 16.dp wypełnienie do krawędzi poziomych (po lewej i po prawej), a potem 8.dp do górnej i dolnej krawędzi treści.

Pamiętaj, że dopełnienie jest stosowane do treści, a nie do samego LazyColumn. W przykładzie powyżej pierwszy element doda 8.dp do górnej krawędzi, ostatni element doda 8.dp do dolnej krawędzi, a wszystkie elementy będą miały 16.dp do lewej i prawej krawędzi.

Odstępy między treściami

Aby dodać odstępy między elementami, możesz użyć funkcji Arrangement.spacedBy(). W przykładzie poniżej między każdym elementem jest dodawane 4.dp miejsca:

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

Podobnie w przypadku LazyRow:

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

W siatkach akceptowane są zarówno rozmieszczenie pionowe, jak i poziome:

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

Klucze elementów

Domyślnie stan każdego elementu jest powiązany z jego pozycją na liście lub siatce. Może to jednak powodować problemy w przypadku zmiany zbioru danych, ponieważ elementy, których pozycja się zmienia, tracą w praktyce każdy zapamiętany stan. W sytuacji, gdy wyobrażasz sobie scenariusz zastosowania funkcji LazyRow w wierszu LazyColumn, gdy wiersz zmieni pozycję elementu, użytkownik utraci pozycję przewijania w tym wierszu.

Aby temu zapobiec, możesz podać stabilny i niepowtarzalny klucz dla każdego elementu, podając blok dla parametru key. Podanie stabilnego klucza umożliwia zachowanie spójności stanu elementu we wszystkich zmianach w zbiorze danych:

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

Podając klucze, pomagasz usłudze Compose prawidłowo obsługiwać zmiany kolejności. Jeśli na przykład Twój element zawiera zapamiętany stan, ustawienia kluczy pozwolą komponentowi Compose przenosić ten stan wraz z elementem, gdy zmienia się jego pozycja.

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

Istnieją jednak ograniczenia dotyczące typów kluczy, których możesz używać jako kluczy elementów. Typ klucza musi być obsługiwany przez mechanizm Bundle, czyli mechanizm Androida służący do zachowywania stanów podczas odtwarzania aktywności. Bundle obsługuje takie typy jak elementy podstawowe, wyliczenia i obiekty Parcelable.

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

Klucz musi być obsługiwany przez Bundle, aby rememberSaveable w komponowalnym elemencie można było przywrócić podczas ponownego tworzenia aktywności lub nawet po przewinięciu z tego elementu i powrocie do niego.

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

Animacje elementów

Jeśli używasz widżetu RecyclerView, wiesz, że animuje on automatycznie zmiany elementów. Skróty zapewniają taką samą funkcjonalność w przypadku zmiany kolejności elementów. Interfejs API jest prosty – wystarczy ustawić modyfikator animateItem dla treści produktu:

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

W razie potrzeby możesz nawet podać niestandardową specyfikację animacji:

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

Pamiętaj, aby podać klucze elementów, dzięki czemu będzie można znaleźć nową pozycję przeniesionego elementu.

Przyklejone nagłówki (funkcja eksperymentalna)

Wzorzec „przyklejonego nagłówka” jest przydatny przy wyświetlaniu list zgrupowanych danych. Poniżej możesz zobaczyć przykład „listy kontaktów” pogrupowanych według pierwszej litery nazwiska każdego kontaktu:

Film przedstawiający telefon, na którym przewijana jest lista kontaktów

Aby utworzyć przyklejony nagłówek za pomocą polecenia LazyColumn, możesz skorzystać z eksperymentalnej funkcji stickyHeader(), która zawiera treść nagłówka:

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

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

Aby uzyskać listę z wieloma nagłówkami, tak jak w przykładzie „lista kontaktów” powyżej, możesz:

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

Reagowanie na pozycję przewijania

Wiele aplikacji musi reagować i wsłuchiwać się w zmiany pozycji przewijania i układu elementów. Komponenty leniwie wczytywane obsługują ten przypadek użycia, podnosząc LazyListState:

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

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

W prostych przypadkach użycia aplikacje zwykle potrzebują informacji tylko o pierwszym widocznym elemencie. W przypadku tych właściwości LazyListState określa właściwości firstVisibleItemIndex i firstVisibleItemScrollOffset.

Jeśli weźmiemy pod uwagę przykład pokazywania i ukrywania przycisku w zależności od tego, czy użytkownik przewinął pierwszy element:

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

Odczyt stanu bezpośrednio w kompozycji jest przydatny, gdy musisz zaktualizować inne kompozycje UI, ale są też sytuacje, w których zdarzenie nie musi być obsługiwane w tej samej kompozycji. Typowym przykładem takiej sytuacji jest wysyłanie zdarzenia analitycznego, gdy użytkownik przewinie stronę i przewinie ją. Aby sprawnie zarządzać tymi informacjami, możemy użyć: snapshotFlow()

val listState = rememberLazyListState()

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

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

LazyListState udostępnia też informacje o wszystkich elementach, które są obecnie wyświetlane, oraz ich granicach na ekranie za pomocą właściwości layoutInfo. Więcej informacji znajdziesz w klasie LazyListLayoutInfo.

Kontrolowanie pozycji przewijania

Oprócz reagowania na pozycję przewijania aplikacje mogą też kontrolować tę pozycję. LazyListState obsługuje to za pomocą funkcji scrollToItem(), która „natychmiast” ustawia pozycję przewijania, oraz animateScrollToItem(), która przewija za pomocą animacji (zwanej też płynnym przewijaniem):

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

Duże zbiory danych (stronicowanie)

Biblioteka stron tymczasowych umożliwia aplikacjom obsługę dużych list elementów oraz wczytywanie i wyświetlanie małych fragmentów listy w razie potrzeby. Obsługa stron w wersji 3.0 i nowszych zapewnia obsługę tworzenia wiadomości za pomocą biblioteki androidx.paging:paging-compose.

Aby wyświetlić listę treści z podziałem na strony, możemy użyć funkcji rozszerzenia collectAsLazyPagingItems(), a następnie przekazać zwrócone dane LazyPagingItems do items() w LazyColumn. Podobnie jak w przypadku obsługi podziału na strony w widokach możesz wyświetlać wskaźniki zastępcze podczas wczytywania danych, sprawdzając, czy item ma wartość 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()
            }
        }
    }
}

Wskazówki dotyczące korzystania z layoutów typu Lazy

Aby mieć pewność, że leniwe układy będą działać prawidłowo, możesz wziąć pod uwagę kilka wskazówek.

Unikaj używania elementów o rozmiarze 0 pikseli.

Może się tak zdarzyć w sytuacjach, gdy np. oczekujesz asynchronicznego pobierania niektórych danych, np. obrazów, aby na późniejszym etapie wypełnić elementy listy. Spowoduje to, że układ Lazy utworzy wszystkie elementy w pierwszym pomiarze, ponieważ ich wysokość wynosi 0 pikseli i wszystkie zmieszczą się w widocznym obszarze. Po załadowaniu elementów i rozwinięciu ich wysokości Leniwe układy odrzucają wszystkie inne elementy, które w rzeczywistości nie mieszczą się w widocznym obszarze. Aby tego uniknąć, musisz ustawić domyślne rozmiary elementów, tak by układ leniwy mógł prawidłowo obliczyć, ile elementów zmieści się w widocznym obszarze:

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

Gdy znasz przybliżony rozmiar elementów po asynchronicznym załadowaniu danych, warto zadbać o to, aby ich rozmiar był taki sam przed i po załadowaniu, na przykład przez dodanie placeholderów. Pomoże to utrzymać prawidłową pozycję przewijania.

Unikaj zagnieżdżania komponentów, które można przewijać w tym samym kierunku

Dotyczy to tylko przypadków zagnieżdżania elementów podrzędnych z możliwością przewijania bez zdefiniowanego rozmiaru wewnątrz innego elementu nadrzędnego z możliwością przewijania w tym samym kierunku. Na przykład próba umieszczenia elementu podrzędnego LazyColumn bez stałej wysokości wewnątrz elementu nadrzędnego Column, który można przewijać w kierunku pionowym:

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

Ten sam efekt można osiągnąć, umieszczając wszystkie funkcje kompozycyjne w jednym nadrzędnym elemencie LazyColumn i przesyłając przy użyciu jego DSL różne typy treści. Umożliwia to przesyłanie pojedynczych elementów oraz wielu elementów listy w jednym miejscu:

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

Pamiętaj, że dozwolone są przypadki, w których zagnieżdżasz różne układy kierunków, na przykład układ nadrzędny Row i podrzędny LazyColumn, które można przewijać:

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

Dotyczy to również przypadków, w których nadal używasz układów o tym samym kierunku, ale dodatkowo ustawiasz stały rozmiar dla zagnieżdżonych elementów podrzędnych:

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

Uważaj na umieszczanie wielu elementów w jednym elemencie

W tym przykładzie druga funkcja lambda emituje 2 elementy w jednym bloku:

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

Lazy layouty będą działać zgodnie z oczekiwaniami – rozmieszczą elementy jeden po drugim, tak jakby były to różne elementy. Występują jednak pewne problemy.

Gdy emitowanych jest kilka elementów w ramach jednego elementu, są one traktowane jako jedna entyfikacja, co oznacza, że nie można ich już komponować indywidualnie. Jeśli jeden element jest widoczny na ekranie, wszystkie elementy odpowiadające temu elementowi muszą zostać zdefiniowane i zmierzone. Jeśli używasz ich zbyt często, może to obniżyć wydajność. W skrajnym przypadku, gdy umieścisz wszystkie elementy w jednym elemencie, stosowanie leniwego układu jest całkowicie sensowne. Umieszczenie większej liczby elementów w jednym elemencie może wpłynąć nie tylko na potencjalne problemy z wydajnością, ale też na działanie usług scrollToItem() i animateScrollToItem().

Istnieją jednak przypadki, w których umieszczanie wielu elementów w jednym elemencie ma sens, np. w przypadku rozdzielaczy w liście. Nie chcesz, aby rozdzielacze zmieniały indeksy przewijania, ponieważ nie powinny być uważane za elementy niezależne. Nie będzie to również miało wpływu na wydajność, ponieważ przegrody są małe. Separator prawdopodobnie musi być widoczny, gdy element jest widoczny, zanim będzie widoczny, dzięki czemu może stanowić część poprzedniego elementu:

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

Warto skorzystać z niestandardowych ustawień

Zwykle listy typu Lazy mają wiele elementów i zajmują więcej miejsca niż element przewijania. Jeśli jednak lista zawiera mało elementów, projekt może mieć bardziej szczegółowe wymagania dotyczące umieszczenia ich w widocznym obszarze.

Aby to zrobić, możesz użyć niestandardowej branży Arrangement i przekazać ją do LazyColumn. W tym przykładzie obiekt TopWithFooter musi tylko zaimplementować metodę arrange. Po pierwsze, uporządkuje elementy jeden po drugim. Po drugie, jeśli łączna użyta wysokość jest mniejsza niż wysokość widocznego obszaru, stopka zostanie umieszczona na dole:

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

Rozważ dodanie contentType

Aby zmaksymalizować wydajność układu opartego na technologii Lazy, od wersji Compose 1.2 możesz dodawać do list lub siatek komponent contentType. Dzięki temu możesz określić typ treści dla każdego elementu układu, gdy tworzysz listę lub siatkę składającą się z różnych typów elementów:

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

Gdy podasz właściwość contentType, funkcja Compose będzie mogła ponownie stosować kompozycje tylko między elementami tego samego typu. Ponieważ ponowne używanie jest bardziej efektywne, gdy komponujesz elementy o podobnej strukturze, podanie typów treści zapewnia, że usługa Compose nie będzie próbować tworzyć elementu typu A na zupełnie innym elemencie typu B. Pomaga to zmaksymalizować korzyści z ponownego wykorzystywania kompozycji i wydajności układu Lazy.

Pomiar skuteczności

Skuteczność układu opartego na technologii Lazy można wiarygodnie mierzyć tylko w trybie wydania i z włączoną optymalizacją R8. W kompilacjach do debugowania leniwe przewijanie układu może wyglądać wolniej. Więcej informacji na ten temat znajdziesz w artykule Wydajność tworzenia wiadomości.