Listy i siatki

Wiele aplikacji musi wyświetlać kolekcje elementów. Z tego dokumentu dowiesz się, jak dzięki Jetpack Compose.

Jeśli wiesz, że Twój przypadek użycia nie wymaga przewijania, możesz użyć prostego Column lub Row (w zależności od kierunku) i wygenerować zawartość każdego elementu, iterując po liście w ten 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().

Lista bezczynności

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 sama nazwa wskazuje, różnica między LazyColumn oraz LazyRow to orientacja, w której użytkownicy układają produkty i przewijają. LazyColumn tworzy listę przewijaną w pionie, a LazyRow tworzy poziomy listę przewijaną.

Komponenty Leniwe różnią się od większości układów w funkcji tworzenia wiadomości. 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")
    }
}

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. Zapoznaj się z LazyListScope tutaj znajdziesz więcej informacji.

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.

Siatki mają te same zaawansowane możliwości interfejsu API co listy, a dodatkowo bardzo podobny do DSL – LazyGridScope.() za opisywanie treści.

Zrzut ekranu telefonu z 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 pozwala określić szerokość elementów, a siatka dopasuje do nich jak najwięcej 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 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ć funkcji siatki, by podać niestandardowe rozpiętości kolumn dla elementów. Określ zakres kolumny za pomocą parametru span metod LazyGridScope DSL itemitems. maxLineSpan, jednej z wartości zakresu spanu, jest szczególnie przydatna, 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")
    }
    // ...
}

Siatka o zmiennym przesunięciu

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. Dostępna szerokość jest dzielona przez liczbę kolumn (lub wierszy w przypadku siatki poziomej), a każdy element zajmuje tę szerokość (lub 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 w pionie siatki ze stałymi kolumnami

Dopełnienie treści

Czasami trzeba dodać wypełnienie wokół krawędzi treści. Komponenty z opóźnionym wczytywaniem umożliwiają przekazywanie niektórych parametrów PaddingValues do parametru contentPadding:

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 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, użyj 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),
) {
    // ...
}

Siatka może jednak być ułożona zarówno w pionie, jak i w poziomie:

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ą w listę lub siatkę. Może to jednak powodować problemy w przypadku zmiany zbioru danych, ponieważ elementy, zmiana pozycji powoduje utratę dowolnego zapamiętanego stanu. 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. Dostarczenie klucza stabilnego umożliwia stan elementu spójność między zmianami w zbiorach 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 zmienić ten stan wraz z elementem, gdy zmieni 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 prymitywne, enums czy Parcelables.

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

Klucz musi być obsługiwany przez zasadę Bundle, tak aby klucz rememberSaveable wewnątrz element kompozycyjny może zostać przywrócony po odtworzeniu aktywności, a nawet gdy przewiniesz obszar poza ten element i przewiniesz do tyłu.

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

Animacje elementów

Jeśli korzystano z widżetu RecyclerView, wiesz, że animuje on element zmian. Skróty mają te same funkcje co przetasowanie elementów. Interfejs API jest prosty – wystarczy ustawić modyfikator animateItemPlacement 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)

Wzór „przyklejony nagłówek” jest przydatny podczas wyświetlania listy pogrupowanych 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 uzyskać nagłówek przypinany za pomocą LazyColumn, możesz użyć eksperymentalnej funkcji stickyHeader(), podając zawartość 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ą jedynie informacji o jako pierwszego widocznego elementu. W tym celu usługa LazyListState udostępnia właściwości firstVisibleItemIndexfirstVisibleItemScrollOffset.

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 UI, ale są też scenariusze, 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 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ą 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 pozycjonujących umożliwia aplikacjom Obsługa dużych list elementów, ładowanie i wyświetlanie małych fragmentów jako niezbędną. Strona Paging w wersji 3.0 i nowszych zapewnia obsługę tworzenia Biblioteka 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ć zduplikowane elementy 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 na przykład oczekuje się, że asynchronicznie Pobrać niektóre dane, np. obrazy, by później 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 funkcja Leniwe układy odrzuci następnie wszystkie inne elementy, które są niepotrzebnie złożone ponieważ nie mieszczą się 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 zachować 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 przewijanych elementów podrzędnych bez wstępnie zdefiniowanego elementu w innym elemencie nadrzędnym 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ąć, pakując wszystkie elementy kompozycyjne w jednym nadrzędnym LazyColumn i przy użyciu jego 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 gdy zagnieżdżasz różne układy kierunkowe, np. przewijany element nadrzędny Row i element podrzędny LazyColumn:

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

Lazy layouty będą działać zgodnie z oczekiwaniami – rozmieszczą elementy jeden po drugim, tak jakby były to różne elementy. Istnieje jednak kilka metod, wielu problemów.

Jeśli w ramach jednego elementu emitowanych jest wiele elementów, są one traktowane jako jeden element, co oznacza, że nie mogą już one być komponowane indywidualnie. 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 problemy z wydajnością, umieszczenie większej liczby elementów w jednym elemencie również z użytkownikami scrollToItem() i animateScrollToItem()

Istnieją jednak przypadki, w których umieszczanie wielu elementów w jednym elemencie jest uzasadnione, na przykład 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. Ponadto wydajność nie zmienia się, ponieważ separatory 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) }
    // ...
}

Rozważ użycie niestandardowych układów

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, zostanie ona pozycjonowana elementy wyświetlane jedna po drugiej. Po drugie, jeśli całkowita używana 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

Zacznij od Compose 1.2, by zmaksymalizować wydajność Lazy układ, rozważ dodanie contentType do list lub siatek. 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 parametr contentType, Compose będzie mógł ponownie używać kompozycji tylko między elementami tego samego typu. Ponieważ ponowne wykorzystanie 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 całkowicie innym elemencie typu B. Pomaga to zmaksymalizować korzyści płynące z ponownego używania kompozycji i skuteczności układu opartego na opóźnionym ładowaniu.

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, leniwy układ może być wolniejsze. Więcej informacji na ten temat znajdziesz w artykule Skuteczność tworzenia wiadomości.