Listeler ve ızgaralar

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

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

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

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

Tembel listeler

Çok sayıda öğe (veya bilinmeyen uzunluktaki bir liste) göstermeniz gerekiyorsa Column gibi bir düzen kullanmak performans sorunlarına neden olabilir. Bunun nedeni, tüm öğelerin görünür olup olmadığına bakılmaksızın derlenmesi ve yerleştirilmesidir.

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

Adından da anlaşılacağı gibi, LazyColumn ile LazyRow arasındaki fark, öğelerin düzenlenme ve kaydırma yönüdür. LazyColumn dikey kaydırmalı bir liste, LazyRow ise yatay kaydırmalı bir liste oluşturur.

Yavaş bileşenler, Oluştur'daki çoğu düzenden farklıdır. Uygulamaların doğrudan kompozit öğeler yayınlamasına olanak tanıyan bir @Composable içerik bloğu parametresi kabul etmek yerine, yavaş bileşenler bir LazyListScope.() bloğu sağlar. Bu LazyListScope bloku, uygulamaların öğe içeriğini açıklamasına olanak tanıyan bir DSL sunar. Ardından, tembel bileşen her öğenin içeriğini düzen ve kaydırma konumu gerektirdiği şekilde eklemekten sorumludur.

LazyListScope DSL

LazyListScope DSL'si, düzendeki öğeleri tanımlamak için çeşitli işlevler sağlar. En basit haliyle, item() tek bir öğe, items(Int) ise 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 itemsIndexed() adlı bir varyantı da vardır. Bu varyant, dizini sağlar. Daha fazla bilgi için lütfen LazyListScope referansına bakın.

Yavaş ızgaralar

LazyVerticalGrid ve LazyHorizontalGrid bileşenleri, öğeleri ızgarada görüntüleme desteği sağlar. 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.

Tablolar, listelerle aynı güçlü API özelliklerine sahiptir ve içeriği açıklamak için çok benzer bir DSL'yi (LazyGridScope.()) kullanır.

Izgara düzeninde fotoğrafları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 sütunu en az 128.dp genişliğinde ayarlamak için GridCells.Adaptive kullanılarak öğeler ızgara şeklinde gösterilmektedir:

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

LazyVerticalGrid, öğeler için bir genişlik belirtmenize olanak tanır. Ardından ızgara, mümkün olduğunca çok sütuna sığdırılır. 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 gerekli sütun sayısını içeren bir GridCells.Fixed örneği sağlayabilirsiniz.

Tasarımınızda yalnızca belirli öğelerin standart dışı boyutlara sahip olması gerekiyorsa öğ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ığından özellikle uyarlanabilir boyutlandırmayı kullanırken kullanışlıdır. 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")
    }
    // ...
}

Eğik ızgara

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

Aşağıdaki snippet, öğe başına 200.dp genişliğinde LazyVerticalStaggeredGrid kullanmanın temel bir örneğidir:

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 sütun sayısı ayarlamak için StaggeredGridCells.Adaptive yerine StaggeredGridCells.Fixed(columns) kullanabilirsiniz. Bu işlem, mevcut genişliği sütun sayısına (veya yatay ızgara için satır sayısına) böler ve her öğenin bu genişliği (veya yatay ızgara için yüksekliği) kaplamasını sağlar:

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, eğik aralıklı dikey ızgara örneği

İçerik dolgusu

Bazen içeriğin kenarlarına dolgu eklemeniz gerekir. Yavaş bileşenler, bunu desteklemek için contentPadding parametresine bazı PaddingValues iletmenize olanak tanır:

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

Bu örnekte, yatay kenarlara (sol ve sağ) 16.dp, ardından içeriğin üst ve alt tarafı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 üst kısmına 8.dp dolgu, son öğe alt kısmına 8.dp dolgu, tüm öğeler ise sol ve sağ tarafına 16.dp dolgu ekler.

İçerik aralığı

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

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

LazyRow için de benzer şekilde:

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

Ancak ı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 öğenin durumu, öğenin listedeki veya tablodaki konumuna göre anahtarlanır. Ancak, konumu değişen öğeler hatırlanan durumu etkili bir şekilde kaybettiği için veri kümesi değişirse bu durum sorunlara neden olabilir. Bir LazyColumn içinde LazyRow senaryosunun olduğunu düşünürseniz satır öğe konumunu değiştirirse kullanıcı satırdaki kaydırma konumunu kaybeder.

Bununla mücadele etmek için her öğe için key parametresine bir blok ekleyerek sabit ve benzersiz bir anahtar sağlayabilirsiniz. Sabit bir anahtar sağlamak, öğe durumunun veri kümesi değişikliklerinde 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ıralamayı doğru şekilde işlemesine yardımcı olursunuz. Örneğin, öğeniz hatırlanan durum içeriyorsa ayar anahtarları, konumu değiştiğinde Oluştur'un bu durumu öğeyle birlikte taşımasına olanak tanır.

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

Ancak öğe anahtarı olarak kullanabileceğiniz türlerle ilgili bir sınırlama vardır. Anahtarın türü, etkinlik yeniden oluşturulduğunda durumları korumak için Android'in mekanizması olan Bundle tarafından desteklenmelidir. Bundle, ilkel, enum veya Parcelables gibi türleri destekler.

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

Anahtar, Bundle tarafından desteklenmelidir. Böylece, etkinlik yeniden oluşturulduğunda veya bu öğeden uzaklaşıp geri geldiğinizde bile, öğe bileşiğinin içindeki rememberSaveable geri yüklenebilir.

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

Öğe animasyonları

RecyclerView widget'ını kullandıysanız öğe değişikliklerini otomatik olarak animasyonlu hale getirdiğini bilirsiniz. Tembel düzenler, öğelerin yeniden sıralanması için aynı işlevi sağlar. API basittir. animateItem değiştiricisini öğe içeriğine ayarlamanız yeterlidir:

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

Gerekirse özel animasyon spesifikasyonu da sağlayabilirsiniz:

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

Taşınan öğenin yeni konumunu bulmak için öğeleriniz için anahtar sağladığınızdan emin olun.

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

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

Bir telefonda kişiler listesinde yukarı ve aşağı kaydırma yapan bir video

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ği gibi birden çok üstbilgi 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 öğesini 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ında, 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 ilk öğeyi geçip geçmediğine bağlı olarak bir düğmeyi gösterme ve gizleme örneğini kullanırsak:

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

Diğer kullanıcı arayüzü bileşenlerini güncellemeniz gerektiğinde durumu doğrudan bileşimde okumak faydalıdır. Ancak etkinliğin aynı bileşimde ele alınması gerekmeyen senaryolar da vardır. Bunun yaygın bir örneği, kullanıcı belirli bir noktanın ötesine geçtiğinde bir Analytics etkinliği göndermektir. Bu işlemi verimli bir şekilde gerçekleştirmek için 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 mülkü aracılığıyla şu anda gösterilen tüm öğeler ve ekrandaki sınırları hakkında da bilgi sağlar. Daha fazla bilgi için LazyListLayoutInfo sınıfına bakın.

Kaydırma konumunu kontrol etme

Uygulamaların kaydırma konumuna tepki vermesinin yanı sıra kaydırma konumunu kontrol edebilmesi de yararlıdır. LazyListState, kaydırma konumunu "hemen" sabitleyen scrollToItem() işlevi ve animasyon (sürükleyici kaydırma olarak da bilinir) kullanarak kaydıran animateScrollToItem() işlevi aracılığıyla bunu 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)

Sayfalama kitaplığı, uygulamaların büyük öğe listelerini desteklemesini sağlar. Bu kitaplık, gerektiğinde listenin küçük parçalarını yükleyip gösterir. Sayfalandırma 3.0 ve sonraki sürümler, androidx.paging:paging-compose kitaplığı aracılığıyla Oluştur 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 Sayfalama desteğine benzer şekilde, item değerinin null olup olmadığını kontrol ederek veriler yüklenirken yer tutucular gösterebilirsiniz:

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

Yavaş düzenleri kullanmayla 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 boyutunda öğ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 durumda, öğelerin yüksekliği 0 piksel olduğu ve tüm öğeler görüntü alanına sığabileceği için, tembel düzenin tüm öğelerini ilk ölçümde oluşturmasına neden olur. Öğeler yüklendikten ve yükseklikleri genişletildikten sonra, tembel düzenler, görüntü alanına sığmadıkları için ilk kez gereksiz yere oluşturulmuş diğer tüm öğeleri atar. Bunu önlemek için öğelerinize varsayılan boyutlandırmayı ayarlamanız gerekir. Böylece, yavaş düzen, görüntü alanına kaç öğenin sığabileceğini doğru şekilde hesaplayabilir:

@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 sayede, kaydırma konumunu doğru tutabilirsiniz.

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

Bu durum yalnızca, önceden tanımlanmış bir boyut olmadan kaydırılabilir alt öğelerin aynı yönde kaydırılabilir başka bir üst öğe içine yerleştirildiği durumlarda 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 sayede tek öğelerin yanı sıra birden fazla liste öğesini tek bir yerden yayınlayabilirsiniz:

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

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

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

Aynı yön düzenlerini kullanmaya devam ettiğiniz ancak iç içe yerleştirilmiş alt öğeler için sabit bir boyut belirlediğiniz durumlarda da:

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

Bir öğeye birden fazla öğe eklemekten kaçının

Bu örnekte, ikinci öğe lambda'sı tek bir blokta 2 öğe yayar:

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. Öğeleri farklı öğelermiş gibi birbiri ardına düzenler. Ancak bunu yapmanın birkaç sorunu vardır.

Birden fazla öğe tek bir öğenin parçası olarak yayınlandığında tek bir varlık olarak ele alınır. Bu nedenle, artık tek tek derlenemezler. 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 eklemenin geçerli kullanım alanları vardır (ör. bir listede ayırıcılar kullanmak). Bağımsız öğeler olarak kabul edilmemeleri gerektiğinden, bölücülerin kaydırma dizelerini değiştirmesini istemezsiniz. Ayrıca, bölücüler küçük olduğundan performans etkilenmez. Bölücünün, önceki öğenin parçası olabilmesi için önceki öğe görünür olduğunda görünür olması gerekir:

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

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

Genellikle, yavaş listeler çok sayıda öğe içerir ve kaydırılabilir kapsayıcının boyutundan daha fazla yer 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. Öncelikle öğeleri birbiri ardına yerleştirir. İkinci olarak, kullanılan toplam yükseklik görüntü alanı yüksekliğinden düşükse altbilgi 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 ekleyebilirsiniz

Compose 1.2'den itibaren, yavaş düzeninizin performansını en üst düzeye çıkarmak için listelerinize veya ızgaralarınıza contentType ekleyebilirsiniz. Bu sayede, birden fazla farklı öğe türünden oluşan bir liste veya ızgara oluşturduğunuzda düzenin her bir öğesinin içerik türünü belirtebilirsiniz:

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

contentType sağladığınızda, Compose yalnızca aynı türdeki öğeler arasında kompozisyonları yeniden kullanabilir. Benzer yapıya sahip öğeler oluşturduğunuzda yeniden kullanma daha verimli olduğundan, içerik türlerini belirtmek, Oluştur'un tamamen farklı bir B türü öğenin üzerine A türü 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

Yavaşlamalı bir düzenin performansını yalnızca yayın modunda ve R8 optimizasyonu etkinken güvenilir bir şekilde ölçebilirsiniz. Hata ayıklama derlemelerinde, gecikmeli düzen kaydırma daha yavaş görünebilir. Bu konuda daha fazla bilgi edinmek için Oluşturma performansı başlıklı makaleyi okuyun.