Listeler ve ızgaralar

Birçok uygulamanın, öğe koleksiyonlarını görüntülemesi gerekir. Bu dokümanda, Jetpack Compose'da bunu nasıl verimli bir şekilde yapabileceğiniz açıklanmaktadır.

Kullanım alanınızın herhangi bir kaydırma gerektirmediğini biliyorsanız (yöne bağlı olarak) basit bir Column veya Row kullanabilir ve aşağıdaki şekilde bir liste üzerinde yineleme yaparak her bir öğenin içeriğini yayınlayabilirsiniz:

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

verticalScroll() değiştiricisini kullanarak Column öğesini kaydırılabilir hale getirebiliriz.

Tembel listeler

Çok sayıda öğe (veya bilinmeyen uzunlukta bir liste) görüntülemeniz gerekiyorsa Column gibi bir düzen kullanmak, performans sorunlarına neden olabilir. Bunun nedeni, tüm öğelerin oluşturulup görünür olup olmadıklarının belirlenmesi ve düzenlenebilmesidir.

Oluştur, yalnızca bileşenin görüntü alanında görünen öğeleri oluşturan ve düzenleyen bir bileşen grubu sağlar. LazyColumn ve LazyRow bu bileşenler arasındadır.

Adından da anlaşılacağı gibi LazyColumn ile LazyRow arasındaki fark, bu kişilerin öğelerini yerleştirdikleri ve kaydırdıkları yöndür. LazyColumn dikey olarak kaydırılan bir liste, LazyRow ise yatay olarak kayan bir liste oluşturur.

Tembel bileşenler, Compose'daki çoğu düzenden farklıdır. Tembel bileşenler, @Composable içerik bloku parametresini kabul ederek uygulamaların doğrudan composable'ları yayımlamasına izin vermek yerine bir LazyListScope.() bloğu sağlar. Bu LazyListScope bloku, uygulamaların öğe içeriğini açıklamasına olanak tanıyan bir DSL sunar. Daha sonra geç bileşeni, düzen ve kaydırma konumunun gerektirdiği şekilde her bir öğenin içeriğini eklemekten sorumludur.

LazyListScope DSL

LazyListScope DSL'si, düzendeki öğeleri açıklamak için çeşitli işlevler sağlar. En temelde, item() tek bir öğe ekler ve items(Int) birden fazla öğe ekler:

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

Ayrıca, List gibi öğe koleksiyonları eklemenize olanak tanıyan çeşitli uzantı işlevleri de vardır. Bu uzantılar, yukarıdaki Column örneğimizi kolayca taşımamızı sağlar:

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

items() uzantı işlevinin, dizini sağlayan itemsIndexed() adlı bir varyantı da vardır. Daha fazla bilgi için lütfen LazyListScope referansını inceleyin.

Tembel ızgaralar

LazyVerticalGrid ve LazyHorizontalGrid composable'lar, öğelerin ızgarada gösterilmesini destekler. Dikey tembel ızgara, öğelerini birden fazla sütuna yayılan dikey olarak kaydırılabilir bir kapsayıcıda gösterirken Tembel yatay ızgaralar yatay eksende aynı davranışa sahip olur.

Izgaralar, listelerle aynı güçlü API özelliklerine sahiptir ve ayrıca içeriği tanımlamak için çok benzer bir DSL (LazyGridScope.()) kullanır.

Fotoğraf ızgarasının gösterildiği bir telefonun ekran görüntüsü

LazyVerticalGrid içindeki columns parametresi ve LazyHorizontalGrid içindeki rows parametresi, hücrelerin sütun veya satırlar halinde nasıl oluşturulduğunu kontrol eder. Aşağıdaki örnekte, her bir sütunu en az 128.dp genişliğinde olacak şekilde ayarlamak için GridCells.Adaptive kullanılarak öğeler bir ızgara içerisinde gösterilmektedir:

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

LazyVerticalGrid, öğeler için genişlik belirtmenizi sağlar. Tablo, mümkün olduğunca çok sütuna sığar. Sütun sayısı hesaplandıktan sonra, kalan genişlik sütunlar arasında eşit olarak dağıtılır. Bu uyarlanabilir boyutlandırma yöntemi, özellikle farklı ekran boyutlarında öğe gruplarını görüntülemek için yararlıdır.

Kullanılacak sütun sayısını tam olarak biliyorsanız bunun yerine, GridCells.Fixed için gerekli sayıda sütun içeren bir örnek sağlayabilirsiniz.

Tasarımınız yalnızca belirli öğelerin standart olmayan boyutlara sahip olmasını gerektiriyorsa öğeler için özel sütun aralıkları sağlamak üzere ızgara desteğini kullanabilirsiniz. LazyGridScope DSL item ve items yöntemlerinin span parametresiyle sütun aralığını belirtin. Aralık kapsamının değerlerinden biri olan maxLineSpan, sütun sayısı sabit olmadığı için özellikle uyarlanabilir boyutlandırma kullandığınızda yararlı olur. Bu örnekte, tam satır kapsamının nasıl sağlanacağı gösterilmektedir:

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

Tembel ızgara

LazyVerticalStaggeredGrid ve LazyHorizontalStaggeredGrid, geç yüklenen, aşamalı bir öğe ızgarası oluşturmanızı sağlayan composable'lardır. Tembel dikey kademeli ızgara, öğelerini birden çok sütuna yayılan ve ayrı ayrı öğelerin farklı yüksekliklerde olmasına olanak tanıyan, dikey olarak kaydırılabilir bir kapsayıcıda görüntüler. Tembel yatay ızgaralar, farklı genişliklere sahip öğelerle yatay eksende aynı davranışa sahiptir.

Aşağıdaki snippet, öğe başına 200.dp genişliğinde LazyVerticalStaggeredGrid kullanımına dair temel bir örnektir:

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

Şekil 1. Tembel dikey ızgara örneği

Sabit bir sayıda sütun ayarlamak için StaggeredGridCells.Adaptive yerine StaggeredGridCells.Fixed(columns) değerini kullanabilirsiniz. Bu işlem, kullanılabilir genişliği sütun sayısına (veya yatay ızgarada satır sayısına) böler ve her bir öğe bu genişliği (veya yatay ızgarada yüksekliği) alır:

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

Compose&#39;da kademeli resim ızgarası
Şekil 2. Sabit sütunlara sahip kademeli dikey ızgara örneği

İçerik dolgusu

Bazen içeriğin kenarlarına dolgu eklemeniz gerekir. Geç bileşenler, bunu desteklemek için contentPadding parametresine bazı PaddingValues öğelerini iletmenizi sağlar:

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

Bu örnekte, yatay kenarlara (sol ve sağ) 16.dp dolgu, ardından içeriğin üst ve alt kısmına 8.dp dolgu ekliyoruz.

Bu dolgunun LazyColumn öğesine değil, içeriğin kendisine uygulandığını lütfen unutmayın. Yukarıdaki örnekte ilk öğe en üstüne 8.dp dolgu ekler, son öğe alt kısmına 8.dp dolgu ekler ve tüm öğelerin sol ve sağ tarafında 16.dp dolgu bulunur.

İçerik aralığı

Öğeler arasında boşluk eklemek için Arrangement.spacedBy() işlevini kullanabilirsiniz. Aşağıdaki örnekte her öğe arasına 4.dp boşluk eklenmiştir:

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

Benzer şekilde LazyRow için:

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

Bununla birlikte ızgaralar hem dikey hem de yatay düzenlemeleri kabul eder:

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

Öğe anahtarları

Varsayılan olarak her bir öğenin durumu, öğenin liste veya tablodaki konumuna göre ayarlanır. Bununla birlikte, konumu değişen öğeler hatırlanan durumları etkili bir şekilde kaybedeceği için bu durum, veri kümesi değişirse sorunlara yol açabilir. LazyRow senaryosunu LazyColumn içinde göz önünde bulundurursanız satır, öğe konumunu değiştirirse kullanıcı satır içindeki kaydırma konumunu kaybeder.

Bununla mücadele etmek amacıyla, her öğe için sabit ve benzersiz bir anahtar sağlayarak key parametresi için bir blok sağlayabilirsiniz. Sabit bir anahtar sağlamak, öğe durumunun veri kümesi değişiklikleri arasında tutarlı olmasını sağlar:

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

Anahtarları sağlayarak, Compose'un yeniden sıralamaları doğru şekilde işlemesine yardımcı olursunuz. Örneğin, öğeniz hatırlandı durumu içeriyorsa anahtarları ayarlamak, Oluştur'un konumu değiştiğinde bu durumu öğeyle birlikte taşımasına olanak tanır.

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

Bununla birlikte, öğe anahtarı olarak kullanabileceğiniz türlerle ilgili bir sınırlama vardır. Anahtarın türü, Android'in Etkinlik yeniden oluşturulduğunda sahip olduğu durumları koruma mekanizması olan Bundle tarafından desteklenmelidir. Bundle; temel öğeler, enumlar veya parseller gibi türleri destekler.

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

composable'ın içindeki rememberSaveable öğesinin, Etkinlik yeniden oluşturulduğunda, hatta bu öğeden ayrılıp geri kaydırdığınızda bile geri yüklenebilmesi için bu anahtarın Bundle tarafından desteklenmesi gerekir.

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

Öğe animasyonları

RecyclerView widget'ını kullandıysanız öğe değişikliklerinin otomatik olarak canlandırıldığını bilirsiniz. Tembel düzenler, öğelerin yeniden sıralanması için aynı işlevi sağlar. API basittir. animateItemPlacement değiştiriciyi öğe içeriği olarak ayarlamanız yeterlidir:

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

Şunlara ihtiyacınız varsa özel animasyon spesifikasyonu bile sağlayabilirsiniz:

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

Taşınan öğenin yeni konumunun bulunabilmesi için öğelerinize anahtar sağladığınızdan emin olun.

Yeniden sıralamaların yanı sıra ekleme ve kaldırma işlemleri için öğe animasyonları şu anda geliştirilme aşamasındadır. Sorunun durumunu 150812265 ile takip edebilirsiniz.

Yapışkan başlıklar (deneysel)

"Yapışkan başlık" kalıbı, gruplandırılmış verilerin listesini görüntülerken faydalıdır. Aşağıda her kişinin baş harfine göre gruplandırılmış bir "kişi listesi" örneği görebilirsiniz:

Kişiler listesinde yukarı ve aşağı kaydırılan bir telefonun videosu

LazyColumn ile sabit bir başlık oluşturmak için başlık içeriğini sağlayan deneysel stickyHeader() işlevini kullanabilirsiniz:

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

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

Yukarıdaki "kişi listesi" örneğinde olduğu gibi, birden çok başlık içeren bir liste oluşturmak için şunları yapabilirsiniz:

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

Kaydırma konumuna tepki verme

Birçok uygulamanın, kaydırma konumu ile öğe düzenindeki değişikliklere tepki vermesi ve bunları dinlemesi gerekir. Tembel bileşenler, LazyListState değerini kaldırarak bu kullanım alanını destekler:

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

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

Basit kullanım alanları için uygulamaların genellikle yalnızca ilk görünür öğeyle ilgili bilgileri bilmesi gerekir. Bunun için LazyListState, firstVisibleItemIndex ve firstVisibleItemScrollOffset özelliklerini sağlar.

Kullanıcının kaydırarak ilk öğeyi geçip geçmediğine bağlı olarak bir düğmeyi gösterme ve gizleme örneğini kullanırsak:

@OptIn(ExperimentalAnimationApi::class)
@Composable
fun MessageList(messages: List<Message>) {
    Box {
        val listState = rememberLazyListState()

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

        // Show the button if the first visible item is past
        // the first item. We use a remembered derived state to
        // minimize unnecessary compositions
        val showButton by remember {
            derivedStateOf {
                listState.firstVisibleItemIndex > 0
            }
        }

        AnimatedVisibility(visible = showButton) {
            ScrollToTopButton()
        }
    }
}

Durumu doğrudan kompozisyonda okumak, diğer kullanıcı arayüzü composable'larını güncellemeniz gerektiğinde faydalıdır. Ancak etkinliğin aynı bileşimde işlenmesinin gerekmediği senaryolarla da karşılaşılabilir. Bunun yaygın bir örneği, kullanıcı ekranı kaydırarak belirli bir noktayı kaydırdıktan sonra bir analiz etkinliği göndermektir. Bunu verimli şekilde yapmak için bir snapshotFlow() kullanabiliriz:

val listState = rememberLazyListState()

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

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

LazyListState, layoutInfo özelliği aracılığıyla şu anda görüntülenmekte olan tüm öğeler ve ekrandaki sınırları hakkında bilgi de sağlar. Daha fazla bilgi için LazyListLayoutInfo sınıfını inceleyin.

Kaydırma konumunu kontrol etme

Bu, kaydırma konumuna tepki vermenin yanı sıra uygulamaların, kaydırma konumunu kontrol edebilmesi için de yararlıdır. LazyListState bunu, kaydırma konumunu "hemen" tutturan scrollToItem() işlevi ve bir animasyon kullanarak kaydıran animateScrollToItem() (yumuşak kaydırma olarak da bilinir) işleviyle destekler:

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

Büyük veri kümeleri (sayfalama)

Çağrı kitaplığı, uygulamaların büyük öğe listelerini desteklemesine olanak tanır. Bu sayede, listenin küçük parçalarını yükleyip görüntüleyebilir. 3.0 ve sonraki sürümler, androidx.paging:paging-compose kitaplığı aracılığıyla Compose desteği sağlar.

Sayfalandırılmış içeriklerin listesini görüntülemek için collectAsLazyPagingItems() uzantı işlevini kullanıp döndürülen LazyPagingItems öğesini LazyColumn içinde items() öğesine iletebiliriz. Görünümlerdeki Sayfalandırma desteğine benzer şekilde, veriler yüklenirken item için null olup olmadığını kontrol ederek yer tutucuları görüntüleyebilirsiniz:

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

Tembel düzenlerin kullanımıyla ilgili ipuçları

Tembel düzenlerinizin gerektiği gibi çalışmasını sağlamak için göz önünde bulundurabileceğiniz birkaç ipucu vardır.

0 piksel boyutlu öğeler kullanmaktan kaçının

Bu durum, örneğin daha sonraki bir aşamada listenizin öğelerini doldurmak için resimler gibi bazı verileri eşzamansız olarak almayı beklediğiniz senaryolarda gerçekleşebilir. Bu durum, 0 piksel yüksekliğinde olduğu ve tüm öğeleri görüntü alanına sığabileceği için Tembel düzenin ilk ölçümde tüm öğelerini oluşturmasına neden olur. Öğeler yüklenip yükseklikleri artırıldıktan sonra Tembel düzenler, ilk seferde gereksiz yere oluşturulan diğer tüm öğeleri siler. Çünkü bunlar görüntü alanına sığmaz. Bu durumdan kaçınmak için öğelerinizin varsayılan boyutlandırmasını ayarlamanız gerekir. Böylece, Tembel düzen, görüntü alanına gerçekten sığabilecek öğe sayısını doğru şekilde hesaplar:

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

Veriler eşzamansız olarak yüklendikten sonra öğelerinizin yaklaşık boyutunu bildiğinizde, öğelerinizin bedeninin yüklemeden önce ve yüklendikten sonra aynı kalmasını sağlamak (ör. yer tutucular eklemek) iyi bir uygulamadır. Bu, kaydırmanın doğru konumda kalmasına yardımcı olur.

Aynı yönde kaydırılabilen bileşenleri iç içe yerleştirmekten kaçının

Bu yalnızca, kaydırılabilir alt öğeleri önceden tanımlanmış bir boyut olmadan, aynı yöne doğru kaydırılabilir başka bir üst öğenin içine yerleştirildiği durumlar için geçerlidir. Örneğin, dikey olarak kaydırılabilir bir Column üst öğesinin içine sabit yüksekliği olmayan bir alt LazyColumn alt öğesi yerleştirmeye çalıştığınızda:

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

Bunun yerine, tüm composable'larınızı bir üst LazyColumn içine sarmalayarak ve farklı içerik türlerini iletmek için DSL'yi kullanarak aynı sonucu elde edebilirsiniz. Bu, hem tek bir öğenin hem de birden çok liste öğesinin tek bir yerde yayınlanmasını sağlar:

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

Farklı yön düzenlerini iç içe yerleştirdiğiniz durumlara (örneğin, kaydırılabilir üst Row ve alt LazyColumn) izin verildiğini unutmayın:

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

Ayrıca, hâlâ aynı yön düzenlerini kullandığınız ancak iç içe yerleştirilmiş alt öğeler için sabit bir boyut ayarladığınız durumlar da buna dahildir:

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

Tek bir öğeye birden çok öğe yerleştirmemeye dikkat edin

Bu örnekte, lambda adlı ikinci öğe bir blokta 2 öğe yayınlar:

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

Tembel düzenler, bunu beklendiği gibi ele alır ve öğeleri farklı öğeler gibi art arda düzenler. Ancak, bunu yaparken bazı sorunlar var.

Bir öğenin parçası olarak yayınlanan birden fazla öğe, tek bir varlık olarak ele alınır. Yani artık ayrı ayrı oluşturulamazlar. Ekranda bir öğe görünür hale gelirse bu öğeye karşılık gelen tüm öğelerin oluşturulması ve ölçülmesi gerekir. Bu özellik, aşırı kullanılırsa performansı düşürebilir. Tüm öğeleri tek bir öğeye yerleştirmek aşırı bir örnek olarak, Tembel düzenlerin kullanılmasının amaçlarını tamamen ortadan kaldırır. Olası performans sorunlarının yanı sıra, bir öğeye daha fazla öğe eklemek scrollToItem() ve animateScrollToItem() işlevlerini de etkiler.

Bununla birlikte, bir öğeye birden fazla öğe yerleştirmek için (liste içinde ayırıcılar kullanma gibi) geçerli kullanım alanları vardır. Ayrı öğeler olarak görülmemeleri gerektiğinden, ayırıcıların kaydırma dizinlerini değiştirmesini istemezsiniz. Ayrıca, ayırıcılar küçük olduğundan performans etkilenmez. Ayırıcı, öğe görünmeden önce görünür olduğunda muhtemelen görünür olmalıdır. Böylece önceki öğenin bir parçası olabilirler:

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

Özel düzenlemeleri kullanmayı düşünün

Tembel listelerde genellikle çok sayıda öğe bulunur ve bu listeler, kaydırma kapsayıcısının boyutundan daha fazlasını kaplar. Bununla birlikte, listeniz az sayıda öğeyle doldurulduğunda tasarımınız, bunların görüntü alanına nasıl yerleştirilmesi gerektiğine dair daha spesifik gereksinimlere sahip olabilir.

Bunu yapmak için özel sektörü Arrangement kullanıp LazyColumn bölümüne aktarabilirsiniz. Aşağıdaki örnekte, TopWithFooter nesnesinin yalnızca arrange yöntemini uygulaması gerekir. İlk olarak, öğeleri birbiri ardına yerleştirir. İkinci olarak, kullanılan toplam yükseklik görüntü alanı yüksekliğinden düşükse alt bilgi en alta yerleştirilir:

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

contentType adlı kullanıcıyı eklemeyi düşünün

Compose 1.2'den başlayarak Lazy düzeninizin performansını en üst düzeye çıkarmak için listelerinize veya ızgaralarınıza contentType ekleyebilirsiniz. Bu, birden çok farklı öğe türünden oluşan bir liste veya ızgara oluşturduğunuz durumlarda, düzenin her bir öğesi için içerik türünü belirtmenize olanak tanır:

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

contentType özelliğini sağladığınızda, Oluştur, besteleri yalnızca aynı türdeki öğeler arasında yeniden kullanabilir. Benzer yapıya sahip öğeler oluşturduğunuzda yeniden kullanmak daha verimli olacağından, içerik türlerinin sağlanması Oluştur'un tamamen farklı bir B tipi öğenin üzerinde A türünde bir öğe oluşturmaya çalışmamasını sağlar. Bu, besteyi yeniden kullanmanın ve Tembel düzen performansınızın avantajlarını en üst düzeye çıkarmanıza yardımcı olur.

Performansı ölçme

Lazy düzeninin performansını yalnızca yayın modunda çalışırken ve R8 optimizasyonunu etkinleştirdiğinizde güvenilir bir şekilde ölçebilirsiniz. Hata ayıklama derlemelerinde, tembel düzen kaydırması daha yavaş görünebilir. Bu hakkında daha fazla bilgi için Performans oluşturma bölümünü okuyun.