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 Twój przypadek użycia nie wymaga przewijania, użyj prostego Column lub Row (w zależności od kierunku) i emituj treść każdego elementu przez powtarzanie listy w taki sposób:

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

Możemy umożliwić przewijanie elementu Column za pomocą modyfikatora verticalScroll().

Leniwe listy

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

Narzędzie Utwórz obejmuje zestaw komponentów, które tylko komponują i rozmieszczają elementy, są widoczne w widocznym obszarze komponentu. Komponenty te obejmują: LazyColumn oraz LazyRow

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

Komponenty leniwie wczytywane różnią się od większości układów w Compose. Zamiast parametr blokady treści @Composable, co pozwala aplikacjom bezpośrednio emitują elementy kompozycyjne, komponenty Lazy oferują blok LazyListScope.(). Ten blok LazyListScope udostępnia 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 w LazyListScope udostępnia kilka funkcji do opisywania elementów w układzie. Ogólnie rzecz biorąc, item() doda jeden element, 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")
    }
}

Dostępnych jest też wiele funkcji rozszerzeń, które umożliwiają dodawanie kolekcji elementów, takich jak List. Dzięki tym rozszerzeniom przeprowadź migrację naszego przykładu Column opisanego 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 dokumentacji 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 telefonu z siatką zdjęć

Parametr columns w: LazyVerticalGrid. i rows parametr w LazyHorizontalGrid. kontrolować sposób tworzenia kolumn lub wierszy z komórek. Poniżej przykład wyświetla elementy w postaci siatki przy użyciu GridCells.Adaptive aby ustawić dla każdej kolumny szerokość co najmniej 128.dp:

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

LazyVerticalGrid pozwala określić szerokość elementów, a siatka dopasuje do nich jak najwięcej kolumn. Pozostała szerokość jest równomiernie rozłożona między kolumnami po obliczeniu liczby kolumn. 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 do użycia, możesz zamiast tego podać wystąpienie GridCells.Fixed zawierający liczbę wymaganych kolumn.

Jeśli przy projektowaniu tylko niektóre elementy mają niestandardowe wymiary, możesz użyć siatki, by podać niestandardowe rozpiętości kolumn dla elementów. Określ zakres kolumny za pomocą parametru span metody LazyGridScope DSL item i items. 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 oraz LazyHorizontalStaggeredGrid to elementy kompozycyjne, które umożliwiają tworzenie leniwie ładowanych, rozłożonych w czasie siatki elementów. W przypadku opóźnionego pionowego siatki o zmiennej szerokości elementy są wyświetlane w przesuwanym pionowo kontenerze, który obejmuje kilka kolumn i umożliwia wyświetlanie poszczególnych elementów o różnej wysokości. W przypadku leniwych siatek poziomych elementy o różnej szerokości zachowują się tak samo na osi poziomej.

Ten fragment kodu to podstawowy przykład użycia elementu LazyVerticalStaggeredGrid o 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 opóźnionego siatki pionowej z przesunięciem

Do ustawienia stałej liczby kolumn możesz użyć opcji StaggeredGridCells.Fixed(columns) zamiast StaggeredGridCells.Adaptive. Spowoduje to podzielenie dostępnej szerokości przez liczbę kolumn (lub wierszy dla poziomą siatkę) i każdy element ma taką szerokość (lub wysokość siatka pozioma):

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()
)
Leniwe rozłożone w siatce obrazów w funkcji Compose
Rysunek 2. Przykład nieciągłej siatki pionowej z stałymi kolumnami

Dopełnienie treści

Czasami trzeba dodać wypełnienie wokół krawędzi treści. Lenistwo umożliwiają przekazywanie części PaddingValues do parametru contentPadding, aby to obsłużyć:

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

W tym przykładzie do poziomych krawędzi (po lewej i po lewej) dodamy dopełnienie (16.dp) w prawo), a potem 8.dp na górę i dół treści.

Pamiętaj, że to wypełnienie jest stosowane do treści, a nie do samego LazyColumn. W przykładzie powyżej pierwszy element doda 8.dp dopełnienie na górze, ostatni element doda 8.dp na dole, a wszystkie elementy będzie zawierać dopełnienie 16.dp po lewej i prawej stronie.

Odstępy między treściami

Aby dodać odstępy między elementami, użyj funkcji Arrangement.spacedBy() W przykładzie poniżej umieszczamy 4.dp odstępu między elementami:

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 produktów

Domyślnie stan każdego elementu jest powiązany z jego pozycją na liście lub siatce. Może to jednak spowodować problemy, jeśli zestaw danych ulegnie zmianie, ponieważ elementy, które zmieniają pozycję, tracą zapamiętany stan. Jeśli wyobrazimy sobie scenariusz LazyRow w ramach LazyColumn, to jeśli wiersz zmieni pozycję elementu, użytkownik straci pozycję przewijania w 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 produktu podczas zmian 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 Bundle, czyli mechanizm Androida do przechowywania stanów podczas ponownego tworzenia aktywności. Bundle obsługuje typy takie jak elementy podstawowe, wyliczenia i klasy 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ą tę samą funkcjonalność w przypadku zmiany kolejności elementów. Interfejs API jest prosty – wystarczy ustawić parametr animateItemPlacement modyfikator treści elementu:

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.

Oprócz zmiany kolejności animacje elementów dotyczące dodawania i usuwania są obecnie w fazie rozwoju. Postępy możesz śledzić w sprawie 150812265.

Przyklejone nagłówki (funkcja eksperymentalna)

Wzorzec „przyklejonego nagłówka” jest przydatny przy wyświetlaniu list zgrupowanych danych. Poniżej znajduje się przykładowa „lista kontaktów” pogrupowana według inicjał:

Film przedstawiający telefon, który przewija się w górę i w dół listy kontaktów

Aby utworzyć przyklejony nagłówek za pomocą funkcji LazyColumn, możesz skorzystać z eksperymentu stickyHeader(). , podając zawartość nagłówka:

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

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

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

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

Reakcja na pozycję przewijania

Wiele aplikacji musi reagować i słuchać zmian pozycji przewijania i układów 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 przypadku prostych zastosowań aplikacje zwykle potrzebują informacji tylko o pierwszym widocznym elemencie. Do tego celu LazyListState zapewnia firstVisibleItemIndex oraz firstVisibleItemScrollOffset usług.

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

Czytanie stanu bezpośrednio w kompozycji jest przydatne, gdy trzeba zaktualizować inne komponenty interfejsu użytkownika, ale zdarzają się też sytuacje, w których zdarzenie nie musi być obsługiwane w tej samej kompozycji. Typowym przykładem jest wysyłanie zdarzenia Analytics po przewinięciu przez użytkownika określonego punktu. Aby rozwiązać ten problem możemy używać snapshotFlow():

val listState = rememberLazyListState()

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

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

LazyListState zawiera również informacje o wszystkich elementach, które są obecnie oraz ich granice na ekranie, za pomocą funkcji layoutInfo. usłudze. 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ą systemu scrollToItem() która „natychmiast” pozycja przewijania i animateScrollToItem() które przewijają się za pomocą animacji (nazywanej 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 docelowych umożliwia aplikacjom Obsługa dużych list elementów, ładowanie i wyświetlanie małych fragmentów jako niezbędną. Paging 3.0 i nowsze wersje obsługują Compose za pomocą biblioteki androidx.paging:paging-compose.

Aby wyświetlić listę treści z podziałem na strony, możemy użyć funkcji collectAsLazyPagingItems() i przekazać zwróconą funkcję LazyPagingItems do items() w naszym LazyColumn. Podobnie jak w przypadku obsługi podziału na strony w widokach możesz wyświetlać substytuty podczas wczytywania danych, sprawdzając, czy item jest 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 na przykład oczekuje się, że asynchronicznie Pobrać niektóre dane, np. obrazy, by później wypełnić elementy listy. W rezultacie układ Lazy skomponowałby wszystkie elementy bo ich wysokość wynosi 0 pikseli i mieści się widoczny obszar. Gdy elementy zostaną załadowane, a ich wysokość zostanie rozszerzona, układy oparte na metodzie Lazy Discard odrzuciłyby wszystkie inne elementy, które zostały niepotrzebnie utworzone podczas pierwszego wczytywania, ponieważ nie mieszczą się one w widocznym obszarze. Aby tego uniknąć: ustaw domyślne rozmiary elementów, tak by układ Lazy mógł czyli jaka jest liczba elementów, które w rzeczywistości mieszczą 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, gdy w ramach elementu nadrzędnego z możliwością przewijania w tym samym kierunku umieszczane są elementy podrzędne z możliwością przewijania bez zdefiniowanego rozmiaru. 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 {
        // ...
    }
}

Taki sam efekt można uzyskać, opakowując wszystkie komponenty w jednym elemencie nadrzędnym LazyColumn i używając jego interfejsu DSL do przekazywania różnych typów treści. Umożliwia to emitowanie pojedynczych elementów i 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 {
        // ...
    }
}

oraz w sytuacjach, w których nadal używasz tych samych układów kierunku, ale masz ustawiony stały rozmiar dla zagnieżdżonych elementów podrzędnych:

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

Nie umieszczaj wielu elementów w jednym elemencie

W tym przykładzie drugi element lambda emituje 2 elementy w jednym bloku:

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

Leniwe układy radzą sobie z tym zgodnie z oczekiwaniami – najpierw układają elementy po drugie, jakby były różnymi elementami. Występują jednak pewne problemy.

Jeśli w ramach jednego elementu emitowanych jest wiele elementów, są one traktowane jako jeden element, co oznacza, że nie można już ich tworzyć pojedynczo. Jeśli jeden staje się widoczny na ekranie, a następnie wszystkie elementy odpowiadające parametrowi taki element musi być skomponowany i zmierzony. Nadmierne używanie tego typu elementów może obniżyć wydajność. W ekstremalnym przypadku umieszczenia wszystkich elementów w jednym elemencie całkowicie rezygnuje z używania leniwego układu. Oprócz potencjalnych problemów z wydajnością umieszczanie większej liczby elementów w jednym elemencie może też zakłócać działanie funkcji 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 separatory zmieniały przewijanie indeksów, ponieważ nie powinny one być uznawane za niezależne elementy. Nie wpłynie to na wydajność, ponieważ rozdzielacze są małe. Separator powinien być prawdopodobnie jeszcze przed wyświetleniem elementu, więc mogą być częścią poprzedniego. element:

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

Zastanów się nad wykorzystaniem niestandardowych aranżacji

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

Aby to osiągnąć, użyj kategorii niestandardowej Arrangement i przekaże ją LazyColumn. W poniższym przykładzie TopWithFooter wystarczy zaimplementować metodę arrange. Po pierwsze, elementy będą ustawiane jeden po drugim. Po drugie, jeśli całkowita używana wysokość jest mniejsza niż wysokość widocznego obszaru spowoduje umieszczenie stopki 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 elementu układu, w przypadku, gdy tworzysz listę lub siatkę składającą się wiele różnych typów produktów:

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

Gdy podasz adres URL contentType W funkcji tworzenia można ponownie używać tylko kompozycji między elementami tego samego typu. Ponowne wykorzystywanie jest bardziej wydajne, nie zawierają elementów o podobnej strukturze, dodanie typu treści zapewnia W trakcie tworzenia wiadomości nie jest podejmowana próba utworzenia elementu typu A na podstawie inny element typu B. Pomaga to zmaksymalizować korzyści płynące z kompozycji ponowne wykorzystanie treści i wydajność układu.

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 kompilowanych wersjach debugujących przewijanie może być wolniejsze. Więcej informacji na ten temat znajdziesz w artykule Skuteczność funkcji tworzenia wiadomości.