Listeler ve ızgaralar

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

Kullanım alanınızın kaydırma gerektirmediğini biliyorsanız basit bir Column veya Row (yöne bağlı olarak) kullanabilir ve aşağıdaki gibi bir listede yineleme 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 öğesini kaydırılabilir hale getirebiliriz. Daha fazla bilgi için Hareketler dokümanları bölümüne bakın.

Tembel listeler

Çok sayıda öğe (veya bilinmeyen uzunlukta bir liste) görüntülemeniz gerekiyorsa tüm öğeler görünür durumda olsa da oluşturulduğundan ve yerleştirileceğinden Column gibi bir düzen kullanmak performans sorunlarına neden olabilir.

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. Bu bileşenler şunlardır: LazyColumn ve LazyRow.

Addan da anlaşılacağı gibi LazyColumn ile LazyRow arasındaki fark, öğelerin yerleştirildiği ve kaydırıldığı yöndür. LazyColumn dikey olarak kayan bir liste, LazyRow ise yatay olarak kayan bir liste oluşturur.

Tembel bileşenler, Oluşturma'daki çoğu düzenden farklıdır. Lazy bileşenleri, @Composable içerik engelleme parametresini kabul ederek uygulamaların doğrudan composable'lar yaymasına izin vermek yerine bir LazyListScope.() bloğu sağlar. Bu LazyListScope bloğu, uygulamaların öğe içeriğini açıklamasına olanak tanıyan bir DSL sunar. Ardından, Tembel bileşen her bir öğenin içeriğini düzen ve kaydırma konumunun gerektirdiği şekilde 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 ve items(Int) birden çok öğ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")
    }
}

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

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

Ayrıca, 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ı, öğeleri ızgarada görüntülemeyi destekler. Tembel dikey ızgara, öğelerini birden çok sütuna yayılmış dikey olarak kaydırılabilir bir kapsayıcıda gösterir. Tembel yatay ızgaralar ise yatay eksende aynı davranışa sahip olur.

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

Fotoğraf ızgarasını gösteren telefonun ekran görüntüsü

LazyHorizontalGrid içindeki LazyVerticalGrid ve rows parametresi, hücrelerin sütun veya satır şeklinde nasıl oluşturulduğunu kontrol eder.columns 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 üzerinde gösterilmektedir:

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

LazyVerticalGrid, öğeler için genişlik belirtmenize olanak tanır. Sonrasında ızgara, 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 öğe gruplarını farklı ekran boyutlarında görüntülemek için yararlıdır.

Kullanılacak sütunların sayısını tam olarak biliyorsanız gerekli sütun sayısını içeren GridCells.Fixed örneğini sağlayabilirsiniz.

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

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

Tembel tablo

LazyVerticalStaggeredGrid ve LazyHorizontalStaggeredGrid geç yüklenen ve kademeli bir öğe ızgarası oluşturmanıza olanak tanıyan composable'lardır. Tembel dikey kademeli ızgara, öğelerini birden çok sütuna yayılan ve bağımsız öğ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şlikle LazyVerticalStaggeredGrid kullanımına ilişkin 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 kademeli dikey ızgara örneği

Sabit bir sütun sayısı ayarlamak için StaggeredGridCells.Adaptive yerine StaggeredGridCells.Fixed(columns) sütununu kullanabilirsiniz. Bu işlem, kullanılabilir genişliği sütun (veya yatay ızgarada satır) sayısına böler ve her öğenin belirtilen genişliği (yatay ızgarada bu yüksekliği) kullanması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 resimlerden oluşan tembel kademeli ızgara
Ş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 değerleri iletmenizi sağlar:

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 kısmına 8.dp dolgu ekliyoruz.

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

İçerik aralığı

Öğeler arasına boşluk eklemek için Arrangement.spacedBy() kullanabilirsiniz. Aşağıdaki örnekte her bir öğenin arasına 4.dp boşluk eklenir:

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

LazyRow için de benzer durum:

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

Öte yandan, ı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 ızgaradaki konumuna göre anahtarlanır. Ancak konumu değişen öğeler hatırlanan durumları etkin bir şekilde kaybettiği için bu durum, veri kümesi değişirse sorunlara yol açabilir. LazyColumn içinde LazyRow senaryosunu hayal ederseniz, satır öğenin konumu değişirse 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ğlayıp key parametresine bir engelleme sağlayabilirsiniz. Sabit 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ıralamaları doğru şekilde işlemesine yardımcı olursunuz. Örneğin, öğeniz hatırlandı durumu içeriyorsa ayar tuşları, Oluşturma'nın 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 durumu saklama mekanizması Bundle tarafından desteklenmelidir. Bundle; temel öğeler, numaralandırmalar veya parsel maddeler gibi türleri destekler.

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

Etkinlik yeniden oluşturulduğunda veya hatta görünümü bu öğeden uzaklaştırıp geri kaydırdığınızda composable içindeki rememberSaveable öğesinin geri yüklenebilmesi için 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 bu widget'ın öğe değişikliklerini otomatik olarak canlandırdığını bilirsiniz. Geç düzenler, öğelerin yeniden sıralanması için aynı işlevi sağlar. API oldukça basittir. Tek yapmanız gereken öğe içeriğinde animateItemPlacement değiştiricisini ayarlamaktır:

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

Aşağıdakileri yapmanız gerekirse özel animasyon spesifikasyonu bile sağlayabilirsiniz:

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

Taşınan öğenin yeni konumunu bulabilmek için öğelerinizin anahtarlarını sağladığınızdan emin olun.

Yeniden sıralama işlemlerinin yanı sıra, ekleme ve kaldırma işlemleri için öğe animasyonları şu anda geliştirme aşamasındadır. İlerleme durumunu 150812265 numaralı sorundan takip edebilirsiniz.

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

"Yapışkan başlık" kalıbı, gruplandırılmış verilerin listelerini 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ı aşağı kayan bir telefonun videosu

LazyColumn ile sabit bir üstbilgi elde etmek için, üstbilgi 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 üstbilgiye sahip bir liste oluşturmak için aşağıdakileri 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 konumuna ve öğe düzeni değişikliklerine tepki vermesi ve bunları dinlemesi gerekir. Lazy (Tembel) bileşenleri, LazyListState'ı 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 bilgi sahibi olması 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üğme 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 bileşimde okumak, kullanıcı arayüzü diğer composable'larını güncellemeniz gerektiğinde faydalıdır ancak etkinliğin aynı bileşim içinde işlenmesinin gerekmediği senaryolar da vardır. Bunun yaygın bir örneği, kullanıcı ekranı kaydırarak belirli bir noktayı geçtikten sonra bir analiz etkinliği göndermektir. Bunu verimli bir şekilde yönetmek 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, görüntülenmekte olan tüm öğeler ve ekrandaki sınırları hakkında da layoutInfo özelliği aracılığıyla bilgi sağlar. Daha fazla bilgi için LazyListLayoutInfo sınıfına göz atın.

Kaydırma konumunu kontrol etme

Kaydırma konumuna tepki vermenin yanı sıra, uygulamaların kaydırma konumunu kontrol edebilmesi de yararlı olur. LazyListState bunu, kaydırma konumunu "hemen" tutturan ve bir animasyon (yumuşak kaydırma olarak da bilinir) kullanarak kaydıran animateScrollToItem() işleviyle scrollToItem() 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 destekleyerek listenin küçük bölümlerini gerektiği şekilde yüklemesini ve görüntülemesini sağlar. 3.0 ve sonraki sürümler, androidx.paging:paging-compose kitaplığı üzerinden Oluşturma desteği sağlar.

Sayfalı içeriğin listesini görüntülemek için collectAsLazyPagingItems() uzantı işlevini kullanabilir ve ardından döndürülen LazyPagingItems öğesini LazyColumn öğemizde items() öğesine geçirebiliriz. Görünümlerdeki Sayfalama desteğine benzer şekilde, item öğesinin null olup olmadığını kontrol ederek veri yüklenirken 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()
            }
        }
    }
}

Geç düzenleri kullanmayla ilgili ipuçları

Geç düzenlerinizin istediğiniz gibi çalışmasını sağlamak için dikkate alabileceğiniz birkaç ipucu vardır.

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

Bu durum, örneğin sonraki bir aşamada listenizdeki öğeleri doldurmak için eşzamansız olarak bazı veriler (ör. resimler) almayı beklediğiniz senaryolarda gerçekleşebilir. Bu, Lazy düzeninin, yükseklikleri 0 piksel olduğu ve tüm öğeleri görüntü alanına sığabileceği için ilk ölçümde tüm öğelerini oluşturmasına neden olur. Öğeler yüklendikten ve yükseklikleri genişletildikten sonra, Tembel düzenler görüntü alanına tam olarak sığamayacağı için ilk kez gereksiz şekilde oluşturulan diğer tüm öğeleri siler. Bunu önlemek için öğelerinize varsayılan boyutlandırmayı ayarlamalısınız. Böylece Geç düzen, görüntü alanına kaç öğenin gerçekten sığabileceğiyle ilgili doğru hesaplamayı yapabilir:

@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 biliyorsanız, bazı yer tutucular ekleyerek öğelerinizin boyutunun yüklenmeden önce ve sonra aynı kalmasını sağlamak iyi bir uygulamadır. Bu, doğru kaydırma konumunun korunmasına yardımcı olur.

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

Bu durum yalnızca, önceden tanımlanmış bir boyutu olmayan, aynı yönde başka bir kaydırılabilir üst öğenin içine yerleştirilen kaydırılabilir alt öğelerin iç içe yerleştirildiği durumlar için geçerlidir. Örneğin, sabit yüksekliği olmayan bir alt LazyColumn öğesini, dikey olarak kaydırılabilir bir Column üst öğesi içine yerleştirmeye çalışıldığında:

// 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 geçirmek için DSL'sini kullanarak aynı sonucu elde edebilirsiniz. Bu, tek bir yerde tek öğelerin yanı sıra birden çok liste öğesinin de 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 durumlarda (ör. kaydırılabilir üst öğe Row ve alt 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 ayarladığınız durumlarda:

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

Tek bir öğeye birden çok öğe koymaya dikkat edin

Bu örnekte, lambda ikinci öğesi 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. Öğeleri, farklı öğelermiş gibi birbiri ardına düzenlerler. Ancak, bunu yaparken bazı sorunlar var.

Bir öğenin parçası olarak birden fazla öğe yayınlandığında bunlar tek bir varlık olarak işlenir. Diğer bir deyişle artık ayrı ayrı oluşturulamazlar. Ekranda bir öğe görünür hale gelirse öğeye karşılık gelen tüm öğelerin oluşturulması ve ölçülmesi gerekir. Bu, aşırı kullanılırsa performansta düşüşe neden olabilir. Tüm öğelerin tek bir öğeye yerleştirilmesi aşırı örnekte, Geç düzenleri kullanma amacına tamamen engel olur. Potansiyel performans sorunlarının yanı sıra, bir öğeye daha fazla öğe yerleştirmek de scrollToItem() ve animateScrollToItem() öğelerini etkiler.

Ancak bir öğeye birden çok öğe yerleştirmek için, bir listenin içinde ayırıcılar bulunması 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. Öğe görünür hale gelmeden önce bir ayırıcının görünür olması gerekir. Böylece bunlar önceki öğenin parçası olabilir:

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

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

Tembel listeler genellikle çok sayıda öğeye sahiptir ve bunlar, kaydırma kapsayıcısının boyutundan daha fazla yer kaplarlar. Bununla birlikte, listeniz birkaç öğeyle doldurulduğunda tasarımınız, bunların görüntü alanında nasıl konumlandırılacağıyla ilgili daha spesifik gereksinimlere sahip olabilir.

Bunu başarmak için özel sektörü Arrangement kullanabilir ve LazyColumn ürününe iletebilirsiniz. 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 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 eklemeyi düşünün

Compose 1.2'den başlayarak tembel düzeninizin performansını en üst düzeye çıkarmak için listelerinize veya ızgaralarınıza contentType eklemeniz önerilir. Bu, birden fazla farklı türde öğe içeren 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 Composer, besteleri yalnızca aynı türdeki öğeler arasında yeniden kullanabilir. Benzer yapıya sahip öğeleri oluşturduğunuzda yeniden kullanmak daha verimli olduğundan, içerik türlerinin sağlanması, Oluştur'un tamamen farklı bir B türü öğenin üzerine A türünde bir öğe oluşturmaya çalışmamasını sağlar. Bu, besteyi yeniden kullanmanın ve geç düzen performansınızın avantajlarını en üst düzeye çıkarmanıza yardımcı olur.

Performansı ölçme

Geç düzenin performansını yalnızca sürüm modunda çalışırken ve R8 optimizasyonu etkin durumdayken güvenilir şekilde ölçebilirsiniz. Hata ayıklama derlemelerinde Geç düzen kaydırması daha yavaş görünebilir. Bu konu hakkında daha fazla bilgi için Oluşturma performansı bölümünü okuyun.