Birçok uygulamanın öğe koleksiyonlarını göstermesi gerekir. Bu belgede, Jetpack Compose'da bunu nasıl verimli bir şekilde yapabileceğiniz açıklanmaktadır.
Kullanım alanınızda kaydırma gerekmediğini biliyorsanız Column
veya Row
(yöne bağlı olarak) gibi basit bir öğe kullanabilir ve her öğenin içeriğini aşağıdaki şekilde bir listede yineleyerek 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.
Lazy lists
Çok sayıda öğe (veya uzunluğu bilinmeyen bir liste) göstermeniz gerekiyorsa Column
gibi bir düzen kullanmak, tüm öğeler görünür olsun veya olmasın oluşturulup düzenleneceğinden performans sorunlarına neden olabilir.
Compose, yalnızca bileşenin görüntü alanında görünür olan öğeleri oluşturan ve yerleştiren bir bileşen grubu sağlar. Bu bileşenler arasında
LazyColumn
ve
LazyRow
yer alır.
Adından da anlaşılacağı gibi, LazyColumn
ve LazyRow
arasındaki fark, öğelerini yerleştirme ve kaydırma yönüdür. LazyColumn
dikey olarak kaydırılan bir liste, LazyRow
ise yatay olarak kaydırılan bir liste oluşturur.
Lazy bileşenleri, Compose'daki çoğu düzenden farklıdır. Lazy bileşenleri, @Composable
içerik blok parametresini kabul etmek ve uygulamaların doğrudan composable'lar yayınlamasına izin vermek yerine LazyListScope.()
bloğu sağlar. Bu LazyListScope
bloğu, uygulamaların öğe içeriklerini açıklamasına olanak tanıyan bir DSL sunar. Lazy bileşeni, düzen ve kaydırma konumunun gerektirdiği şekilde her öğenin içeriğini eklemekten sorumludur.
LazyListScope
DSL
LazyListScope
DSL'si, düzendeki öğeleri tanımlamak için çeşitli işlevler sunar. En temel düzeyde,
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ğini kolayca taşımamıza olanak tanır:
/** * import androidx.compose.foundation.lazy.items */ LazyColumn { items(messages) { message -> MessageRow(message) } }
Dizini sağlayan items()
uzantı işlevinin itemsIndexed()
adlı bir varyantı da vardır. Daha ayrıntılı bilgi için lütfen
LazyListScope
referansını inceleyin.
Lazy grids
LazyVerticalGrid
ve LazyHorizontalGrid
composable'ları, öğelerin ızgarada gösterilmesini destekler. LazyVerticalGrid, öğelerini dikey olarak kaydırılabilir bir kapsayıcıda birden fazla sütuna yayılmış şekilde gösterirken LazyHorizontalGrid, yatay eksende aynı davranışı sergiler.
Izgaralar, listelerle aynı güçlü API özelliklerine sahiptir ve içerikleri tanımlamak için çok benzer bir DSL (alan özgü dil) kullanır.
LazyGridScope.()
LazyVerticalGrid
içindeki columns
parametresi ve LazyHorizontalGrid
içindeki rows
parametresi, hücrelerin sütun veya satır olarak nasıl oluşturulacağını kontrol eder. Aşağıdaki örnekte, her sütunun en az 128.dp
genişliğinde olması için GridCells.Adaptive
kullanılarak öğeler bir ızgarada 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ütun sığdırır. Kalan genişlik, sütun sayısı hesaplandıktan sonra 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östermek için yararlıdır.
Kullanılacak sütunların tam sayısını 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.
span
ve items
yöntemlerinin span
parametresiyle sütun aralığını belirtin.LazyGridScope DSL
item
maxLineSpan
, aralık kapsamının değerlerinden biri olup özellikle uyarlanabilir boyutlandırma kullanırken yararlıdır. Bunun nedeni, sütun sayısının sabit olmamasıdır.
Bu örnekte, tam 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") } // ... }
Geç yüklenen kademeli ızgara
LazyVerticalStaggeredGrid
ve
LazyHorizontalStaggeredGrid
, öğelerin geç yüklenen, kademeli bir ızgarasını oluşturmanıza olanak tanıyan composable'lardır.
Dikey olarak kademeli bir ızgara, öğelerini dikey olarak kaydırılabilir ve birden fazla sütuna yayılan bir kapsayıcıda gösterir. Bu kapsayıcı, öğelerin farklı yüksekliklerde olmasına olanak tanır. Yatay eksende tembel yatay ızgaralar, farklı genişlikteki öğelerle aynı davranışa sahiptir.
Aşağıdaki snippet, LazyVerticalStaggeredGrid
öğesini 200.dp
genişliğinde 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() )
Sabit sayıda sütun ayarlamak için StaggeredGridCells.Adaptive
yerine StaggeredGridCells.Fixed(columns)
kullanabilirsiniz.
Bu, kullanılabilir genişliği sütun sayısına (veya yatay bir ızgara için satır sayısına) böler ve her öğenin bu genişliği (veya yatay bir ı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() )

İçerik dolgusu
Bazen içeriğin kenarlarına dolgu eklemeniz gerekir. Lazy bileşenler, bunu desteklemek için PaddingValues
öğelerini contentPadding
parametresine iletmenize olanak tanır:
LazyColumn( contentPadding = PaddingValues(horizontal = 16.dp, vertical = 8.dp), ) { // ... }
Bu örnekte, yatay kenarlara (sol ve sağ) 16.dp
, içeriğin üst ve alt kısmına ise 8.dp
dolgu ekliyoruz.
Bu dolgunun LazyColumn
için değil, içerik için uygulandığını lütfen unutmayın. Yukarıdaki örnekte, ilk öğenin üst kısmına 8.dp
dolgu, son öğenin alt kısmına 8.dp
dolgu eklenir ve tüm öğelerin sol ve sağ kısımlarında 16.dp
dolgu bulunur.
Başka bir örnek olarak, Scaffold
'nın PaddingValues
değerini LazyColumn
'nın contentPadding
değerine iletebilirsiniz. Uçtan uca kılavuzuna bakın.
İçerik aralığı
Öğeler arasına boşluk eklemek için Arrangement.spacedBy()
kullanabilirsiniz.
Aşağıdaki örnekte, her öğe arasına 4.dp
boşluk eklenmektedir:
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, listedeki veya tablodaki konumuna göre belirlenir. Ancak bu durum, veri kümesi değiştiğinde sorunlara neden olabilir. Çünkü konumu değişen öğeler, hatırlanan durumlarını kaybeder. LazyRow
içinde LazyColumn
senaryosunu düşünürsek satır öğe konumunu değiştirirse kullanıcının satır içindeki kaydırma konumu kaybolur.
Bununla mücadele etmek için her öğe için sabit ve benzersiz bir anahtar sağlayarak key
parametresini engelleyebilirsiniz. Kararlı bir anahtar sağlamak, öğe durumunun veri kümesi değişiklikleri genelinde 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ırlanan durumu içeriyorsa anahtarları ayarlamak, Compose'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() } } }
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 kullandığı mekanizma olan Bundle
tarafından desteklenmelidir. Bundle
, ilkel türler, numaralandırmalar veya Parcelable'lar gibi türleri destekler.
LazyColumn { items(books, key = { // primitives, enums, Parcelable, etc. }) { // ... } }
Öğenin composable'ı içindeki rememberSaveable
, etkinlik yeniden oluşturulduğunda veya bu öğeden uzaklaşıp geri kaydırdığınızda 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 öğe değişikliklerini otomatik olarak animasyonla gösterdiğini bilirsiniz.
Lazy düzenler, öğelerin yeniden sıralanması için aynı işlevselliği sağlar.
API basittir. Yalnızca animateItem
değiştiricisini öğe içeriğine ayarlamanız gerekir:
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()) { // ... } } }
Aşağıdaki durumlarda ö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 konumunun bulunabilmesi için öğelerinizin anahtarlarını sağladığınızdan emin olun.
Örnek: Geç yüklenen listelerdeki öğeleri canlandırma
Compose ile, tembel listelerdeki öğelerde yapılan değişiklikleri canlandırabilirsiniz. Aşağıdaki snippet'ler birlikte kullanıldığında, tembel liste öğeleri eklenirken, kaldırılırken ve yeniden sıralanırken animasyonlar uygulanır.
Bu snippet, öğeler eklendiğinde, kaldırıldığında veya yeniden sıralandığında animasyonlu geçişler içeren bir dizeler listesi görüntüler:
@Composable fun ListAnimatedItems( items: List<String>, modifier: Modifier = Modifier ) { LazyColumn(modifier) { // Use a unique key per item, so that animations work as expected. items(items, key = { it }) { ListItem( headlineContent = { Text(it) }, modifier = Modifier .animateItem( // Optionally add custom animation specs ) .fillParentMaxWidth() .padding(horizontal = 8.dp, vertical = 0.dp), ) } } }
Kodla ilgili önemli noktalar
ListAnimatedItems
, öğeler değiştirildiğinde animasyonlu geçişlerleLazyColumn
içinde bir dizi dize gösterir.items
işlevi, listedeki her öğeye benzersiz bir anahtar atar. Compose, öğeleri izlemek ve konumlarındaki değişiklikleri belirlemek için anahtarları kullanır.ListItem
, her liste öğesinin düzenini tanımlar. Öğenin ana içeriğini tanımlayan birheadlineContent
parametresi alır.animateItem
değiştiricisi, öğe ekleme, kaldırma ve taşıma işlemlerine varsayılan animasyonları uygular.
Aşağıdaki snippet'te, öğe ekleme ve kaldırma kontrollerinin yanı sıra önceden tanımlanmış bir listeyi sıralama kontrollerini içeren bir ekran gösterilmektedir:
@Composable private fun ListAnimatedItemsExample( data: List<String>, modifier: Modifier = Modifier, onAddItem: () -> Unit = {}, onRemoveItem: () -> Unit = {}, resetOrder: () -> Unit = {}, onSortAlphabetically: () -> Unit = {}, onSortByLength: () -> Unit = {}, ) { val canAddItem = data.size < 10 val canRemoveItem = data.isNotEmpty() Scaffold(modifier) { paddingValues -> Column( modifier = Modifier .padding(paddingValues) .fillMaxSize() ) { // Buttons that change the value of displayedItems. AddRemoveButtons(canAddItem, canRemoveItem, onAddItem, onRemoveItem) OrderButtons(resetOrder, onSortAlphabetically, onSortByLength) // List that displays the values of displayedItems. ListAnimatedItems(data) } } }
Kodla ilgili önemli noktalar
ListAnimatedItemsExample
, öğe ekleme, kaldırma ve sıralama kontrollerini içeren bir ekran gösterir.onAddItem
veonRemoveItem
, listeden öğe eklemek ve kaldırmak içinAddRemoveButtons
'ye iletilen lambda ifadeleridir.resetOrder
,onSortAlphabetically
veonSortByLength
, listedeki öğelerin sırasını değiştirmek içinOrderButtons
'ye iletilen lambda ifadeleridir.
AddRemoveButtons
simgesinde "Ekle" ve "Kaldır" düğmeleri gösterilir. Düğmeleri etkinleştirir/devre dışı bırakır ve düğme tıklamalarını işler.OrderButtons
, listenin yeniden sıralanması için düğmeleri gösterir. Sırayı sıfırlamak ve listeyi uzunluğa veya alfabetik olarak sıralamak için lambda işlevlerini alır.ListAnimatedItems
,ListAnimatedItems
composable'ını çağırarakdata
listesini geçirir ve animasyonlu dize listesini gösterir.data
başka bir yerde tanımlanmışsa.
Bu snippet, Öğe Ekle ve Öğe Sil düğmelerinin bulunduğu bir kullanıcı arayüzü oluşturur:
@Composable private fun AddRemoveButtons( canAddItem: Boolean, canRemoveItem: Boolean, onAddItem: () -> Unit, onRemoveItem: () -> Unit ) { Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center ) { Button(enabled = canAddItem, onClick = onAddItem) { Text("Add Item") } Spacer(modifier = Modifier.padding(25.dp)) Button(enabled = canRemoveItem, onClick = onRemoveItem) { Text("Delete Item") } } }
Kodla ilgili önemli noktalar
AddRemoveButtons
, listede ekleme ve kaldırma işlemleri yapmak için bir düğme satırı gösterir.canAddItem
vecanRemoveItem
parametreleri, düğmelerin etkin durumunu kontrol eder.canAddItem
veyacanRemoveItem
yanlışsa ilgili düğme devre dışı bırakılır.onAddItem
veonRemoveItem
parametreleri, kullanıcı ilgili düğmeyi tıkladığında yürütülen lambdalardır.
Son olarak, bu snippet'te listeyi sıralamak için üç düğme (Sıfırla, Alfabetik ve Uzunluk) gösterilir:
@Composable private fun OrderButtons( resetOrder: () -> Unit, orderAlphabetically: () -> Unit, orderByLength: () -> Unit ) { Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center ) { var selectedIndex by remember { mutableIntStateOf(0) } val options = listOf("Reset", "Alphabetical", "Length") SingleChoiceSegmentedButtonRow { options.forEachIndexed { index, label -> SegmentedButton( shape = SegmentedButtonDefaults.itemShape( index = index, count = options.size ), onClick = { Log.d("AnimatedOrderedList", "selectedIndex: $selectedIndex") selectedIndex = index when (options[selectedIndex]) { "Reset" -> resetOrder() "Alphabetical" -> orderAlphabetically() "Length" -> orderByLength() } }, selected = index == selectedIndex ) { Text(label) } } } } }
Kodla ilgili önemli noktalar
OrderButtons
, kullanıcıların listede bir sıralama yöntemi seçmesine veya liste sırasını sıfırlamasına olanak tanımak içinSingleChoiceSegmentedButtonRow
simgesini gösterir. ASegmentedButton
bileşeni, bir seçenek listesinden tek bir seçenek belirlemenizi sağlar.resetOrder
,orderAlphabetically
veorderByLength
, ilgili düğme seçildiğinde yürütülen lambda işlevleridir.selectedIndex
durum değişkeni, seçilen seçeneği takip eder.
Sonuç
Bu videoda, öğeler yeniden sıralandığında önceki snippet'lerin sonucu gösterilmektedir:
Sabit başlıklar (deneysel)
"Sabit başlık" kalıbı, gruplandırılmış veri listelerini görüntülerken faydalıdır. Aşağıda, her kişinin adının ilk harfine göre gruplandırılmış bir "kişi listesi" örneğini görebilirsiniz:
LazyColumn
ile sabit bir başlık elde etmek için deneysel stickyHeader()
işlevini kullanabilir ve başlık içeriğini sağlayabilirsiniz:
@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 fazla ü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 konumuna ve öğe düzeni değişikliklerine tepki vermesi ve bunları dinlemesi gerekir.
Lazy bileşenleri, LazyListState
öğesini yükselterek 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) { // ... } }
Uygulamaların, basit kullanım alanlarında genellikle yalnızca ilk görünür öğe hakkında bilgi sahibi olması gerekir. Bu durumda 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() } } }
Durumu doğrudan kompozisyonda okumak, diğer kullanıcı arayüzü composable'larını güncellemeniz gerektiğinde yararlıdır. Ancak etkinliğin aynı kompozisyonda işlenmesi gerekmeyen senaryolar da vardır. Bunun yaygın bir örneği, kullanıcı belirli bir noktayı geçtikten sonra bir analiz etkinliği göndermektir. Bu durumu verimli bir şekilde ele almak 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
özelliği aracılığıyla, şu anda görüntülenen tüm öğeler ve ekrandaki sınırları hakkında da bilgi sağlar. Daha fazla bilgi için LazyListLayoutInfo
sınıfına göz atı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 "anında" sabitleyen scrollToItem()
işlevi ve animasyon kullanarak kaydıran animateScrollToItem()
(aynı zamanda sorunsuz kaydırma olarak da bilinir) 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)
Paging kitaplığı, uygulamaların gerektiğinde listenin küçük parçalarını yükleyip görüntüleyerek büyük öğe listelerini desteklemesini sağlar. Paging 3.0 ve sonraki sürümler, androidx.paging:paging-compose
kitaplığı aracılığıyla Compose desteği sunar.
Sayfalandırılmış içeriklerin listesini göstermek için collectAsLazyPagingItems()
uzantı işlevini kullanabiliriz. Ardından, döndürülen LazyPagingItems
değerini LazyColumn
içindeki items()
'ye iletebiliriz. Görünümlerdeki sayfalama desteğine benzer şekilde, item
özelliğinin 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() } } } }
Lazy layouts'u kullanmayla ilgili ipuçları
Lazy düzenlerinizin beklendiği gibi çalışmasını sağlamak için dikkate alabileceğiniz birkaç ipucu vardır.
0 piksel boyutlu öğeler kullanmaktan kaçının
Örneğin, listenizdeki öğeleri daha sonra doldurmak için resim gibi bazı verileri eşzamansız olarak almayı beklediğiniz senaryolarda bu durum yaşanabilir. Bu durumda, Lazy düzen, tüm öğelerinin yüksekliği 0 piksel olduğundan ve hepsini görüntü alanına sığdırabileceğinden, tüm öğelerini ilk ölçümde oluşturur. Öğeler yüklendikten ve yükseklikleri genişletildikten sonra Lazy düzenler, ilk seferde gereksiz yere oluşturulan diğer tüm öğeleri görüntü alanına sığamayacakları için atar. Bunu önlemek için, Lazy düzeninin görüntü alanına kaç öğenin sığabileceğiyle ilgili doğru hesaplamayı yapabilmesi için öğelerinizde varsayılan boyutlandırmayı ayarlamanız gerekir:
@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 öğelerinizin boyutunun yüklemeden önce ve sonra aynı kalmasını sağlamak iyi bir uygulamadır. Örneğin, bazı yer tutucular ekleyebilirsiniz. Bu, doğru kaydırma konumunun korunmasına yardımcı olur.
Aynı yönde kaydırılabilir bileşenleri iç içe yerleştirmekten kaçının
Bu yalnızca, önceden tanımlanmış bir boyutu olmayan kaydırılabilir alt öğelerin aynı yönde kaydırılabilir başka bir üst öğenin içine yerleştirildiği durumlarda geçerlidir. Örneğin, dikey olarak kaydırılabilir bir üst öğenin Column
içine sabit yüksekliği olmayan bir alt öğe LazyColumn
yerleştirmeye çalışmak:
// throws IllegalStateException Column( modifier = Modifier.verticalScroll(state) ) { LazyColumn { // ... } }
Bunun yerine, tüm composable'larınızı tek bir üst LazyColumn
içine sarmalayarak ve farklı içerik türlerini iletmek için DSL'sini kullanarak aynı sonucu elde edebilirsiniz. Bu, tek öğelerin ve birden fazla liste öğesinin tek bir yerden 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 durumların (ö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 öğelere sabit bir boyut da ayarladığınız durumlar:
Column( modifier = Modifier.verticalScroll(scrollState) ) { LazyColumn( modifier = Modifier.height(200.dp) ) { // ... } }
Tek bir öğeye birden fazla öğe yerleştirmemeye dikkat edin.
Bu örnekte, ikinci öğe lambda'sı tek bir blokta 2 öğe yayınlar:
LazyVerticalGrid( columns = GridCells.Adaptive(100.dp) ) { item { Item(0) } item { Item(1) Item(2) } item { Item(3) } // ... }
Lazy düzenler bunu beklendiği gibi ele alır. Öğeleri farklı öğelermiş gibi art arda düzenler. Ancak bunu yaparken dikkat etmeniz gereken birkaç nokta 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 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 özellik aşırı kullanıldığında performansı olumsuz etkileyebilir. Tüm öğeleri tek bir öğeye yerleştirme gibi uç durumlarda, tembel düzenlerin kullanılma amacı tamamen ortadan kalkar. Tek bir öğeye daha fazla öğe eklemek, olası performans sorunlarının yanı sıra scrollToItem()
ve animateScrollToItem()
ile de etkileşimi engeller.
Ancak, bir öğeye birden fazla öğe yerleştirmenin geçerli kullanım alanları vardır. Örneğin, bir listenin içinde ayırıcılar kullanmak. Bölücülerin bağımsız öğeler olarak değerlendirilmemesi gerektiğinden kaydırma dizinlerini değiştirmesini istemiyorsunuz. Ayrıca, ayırıcılar küçük olduğundan performans etkilenmez. Önündeki öğe görünür olduğunda ayırıcının da görünür olması gerekir. Bu nedenle, ayırıcılar önceki öğenin bir parçası olabilir:
LazyVerticalGrid( columns = GridCells.Adaptive(100.dp) ) { item { Item(0) } item { Item(1) Divider() } item { Item(2) } // ... }
Özel düzenlemeleri kullanmayı düşünün
Genellikle tembel listelerde çok sayıda öğe bulunur ve bunlar kaydırma kapsayıcısının boyutundan daha fazla yer kaplar. Ancak listeniz az sayıda öğeyle doldurulduğunda tasarımınız, bu öğelerin görünüm alanında nasıl konumlandırılması gerektiği konusunda daha spesifik gereksinimlere sahip olabilir.
Bunu yapmak için özel sektör Arrangement
kullanabilir ve bunu LazyColumn
'ye iletebilirsiniz. Aşağıdaki örnekte, TopWithFooter
nesnesinin yalnızca arrange
yöntemini uygulaması gerekir. İlk olarak, öğeleri art arda konumlandırır. İkinci olarak, kullanılan toplam yükseklik, görünüm alanı yüksekliğinden düşükse altbilgi en altta konumlandırılır:
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üşünebilirsiniz.
Compose 1.2'den itibaren, Lazy düzeninizin performansını en üst düzeye çıkarmak için listelerinize veya ızgaralarınıza contentType
eklemeyi düşünebilirsiniz. Bu sayede, birden fazla farklı türde öğeden oluşan bir liste veya ızgara oluşturduğunuz durumlarda düzenin her öğesi için içerik türünü belirtebilirsiniz:
LazyColumn { items(elements, contentType = { it.type }) { // ... } }
contentType
öğesini sağladığınızda, Compose yalnızca aynı türdeki öğeler arasında kompozisyonları yeniden kullanabilir. Benzer yapıdaki öğeleri oluştururken yeniden kullanmak daha verimli olduğundan, içerik türlerini sağlamak Oluşturma'nın A türündeki bir öğeyi tamamen farklı bir B türündeki öğenin üzerine oluşturmaya çalışmamasını sağlar. Bu, kompozisyonu yeniden kullanmanın ve Lazy 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ıştırırken ve R8 optimizasyonu etkinleştirilmişken güvenilir bir şekilde ölçebilirsiniz. Hata ayıklama derlemelerinde, Lazy düzeninde kaydırma daha yavaş görünebilir. Bu konu hakkında daha fazla bilgi için Oluşturma performansı başlıklı makaleyi inceleyin.
Ek kaynaklar
- Sonlu kaydırılabilir liste oluşturma
- Kaydırılabilir ızgara oluşturma
- İç içe yerleştirilmiş kaydırma öğelerini listede görüntüleme
- Yazarken listeyi filtreleme
- Listeler ve sayfalama ile verileri geç yükleme
- Birden fazla öğe türü kullanarak liste oluşturma
- Video: Oluşturma bölümünde listeler
Sizin için önerilenler
- Not: JavaScript kapalıyken bağlantı metni gösterilir.
RecyclerView
öğesini Lazy list'e taşıma- Compose'da kullanıcı arayüzü durumunu kaydetme
- Jetpack Compose için Kotlin