시배's Android
Compose Docs | Lists and grids 본문
목록 및 그리드 | Jetpack Compose | Android Developers
목록 및 그리드 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. 많은 앱에서 항목의 컬렉션을 표시해야 합니다. 이 문서에서는 Jetpack Compose에서 이 작업을 효
developer.android.com
Lazy lists
많은 수의 항목(또는 길이를 알 수 없는 목록)을 표시해야 하는 경우 열과 같은 레이아웃을 사용하면 모든 항목이 표시 여부와 관계없이 구성되고 배치되므로 성능 문제가 발생할 수 있습니다.
Compose는 컴포넌트의 뷰포트에 표시되는 항목만 컴포넌트를 작성하고 레이아웃하는 컴포넌트 세트를 제공합니다. 이러한 컴포넌트에는 LazyColumn과 LazyRow가 포함됩니다.
이름에서 알 수 있듯이 LazyColumn과 LazyRow의 차이점은 항목을 배치하고 스크롤하는 방향입니다. LazyColumn은 세로로 스크롤되는 목록을 생성하고, LazyRow는 가로로 스크롤되는 목록을 생성합니다.
Lazy 컴포넌트는 Compose의 대부분의 레이아웃과 다릅니다. 컴포저블 콘텐츠 블록 매개변수를 허용하여 앱이 컴포저블을 직접 방출할 수 있도록 허용하는 대신, Lazy 컴포넌트는 LazyListScope.() 블록을 제공합니다. 이 LazyListScope 블록은 앱이 항목 내용을 설명할 수 있는 DSL을 제공합니다. 그런 다음 Lazy 컴포넌트는 레이아웃과 스크롤 위치에 따라 각 항목의 콘텐츠를 추가합니다.
LazyListScope DSL
LazyListScope의 DSL은 레이아웃의 항목을 설명하기 위한 여러 함수를 제공합니다. 가장 기본적인 item()은 단일 항목을 추가하고 item(Int)은 여러 항목을 추가합니다:
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")
}
}
/**
* import androidx.compose.foundation.lazy.items
*/
LazyColumn {
items(messages) { message ->
MessageRow(message)
}
}
Lazy grids
LazyVerticalGrid 및 LazyHorizontalGrid 컴포저블은 그리드에 항목을 표시하는 기능을 지원합니다. Lazy 세로 그리드는 여러 열에 걸쳐 세로로 스크롤 가능한 컨테이너에 항목을 표시하고, Lazy 가로 그리드는 가로 축에서 동일한 동작을 수행합니다.
그리드에는 목록과 동일한 강력한 API 기능이 있으며, 콘텐츠를 설명하는 데 매우 유사한 DSL인 LazyGridScope.()를 사용합니다.

LazyVerticalGrid(
columns = GridCells.Adaptive(minSize = 128.dp)
) {
items(photos) { photo ->
PhotoItem(photo)
}
}
LazyVerticalGrid를 사용하면 항목의 너비를 지정하면 그리드가 가능한 한 많은 열에 맞도록 할 수 있습니다. 열 수가 계산된 후 남은 너비는 열 간에 균등하게 분배됩니다. 이 적응형 크기 조정 방식은 다양한 화면 크기에 걸쳐 항목 집합을 표시할 때 특히 유용합니다.
사용할 열의 정확한 양을 알고 있는 경우 대신 필요한 열 수를 포함하는 GridCells.Fixed 인스턴스를 제공할 수 있습니다.
디자인에 특정 항목에만 비표준 치수가 필요한 경우 그리드 지원을 사용하여 항목에 대한 사용자 지정 열 스팬을 제공할 수 있습니다. LazyGridScope DSL 항목 및 항목 메서드의 span 매개 변수를 사용하여 열 범위를 지정합니다. 스팬 범위의 값 중 하나인 maxLineSpan은 열 수가 고정되어 있지 않으므로 적응형 크기 조정을 사용할 때 특히 유용합니다. 이 예는 전체 행 범위를 제공하는 방법을 보여줍니다:
LazyVerticalGrid(
columns = GridCells.Adaptive(minSize = 30.dp)
) {
item(span = {
// LazyGridItemSpanScope:
// maxLineSpan
GridItemSpan(maxLineSpan)
}) {
CategoryCard("Fruits")
}
// ...
}
Lazy staggered grid
LazyVerticalStaggeredGrid와 LazyHorizontalStaggeredGrid는 항목의 지연 로드, 엇갈린 그리드를 만들 수 있는 컴포저블입니다. 지연 수직 엇갈림 그리드는 여러 열에 걸쳐 세로로 스크롤 가능한 컨테이너에 항목을 표시하고 개별 항목의 높이를 다르게 설정할 수 있습니다. 지연 가로 격자는 가로 축에서 너비가 다른 항목에 대해 동일한 동작을 수행합니다.
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()
)
Item keys
기본적으로 각 항목의 상태는 목록 또는 그리드에서 항목의 위치에 대해 키가 지정됩니다. 그러나 데이터 세트가 변경되면 위치가 변경된 항목은 기억된 상태를 사실상 잃게 되므로 문제가 발생할 수 있습니다. LazyColumn 내의 LazyRow 시나리오를 상상해 보면, 행이 항목 위치를 변경하면 사용자는 행 내에서 스크롤 위치를 잃게 됩니다.
이 문제를 방지하기 위해 각 항목에 대해 안정적이고 고유한 키를 제공하여 키 매개변수에 블록을 제공할 수 있습니다. 안정적인 키를 제공하면 데이터 집합 변경 시에도 항목 상태가 일관되게 유지됩니다:
LazyColumn {
items(
items = messages,
key = { message ->
// Return a stable + unique key for the item
message.id
}
) { message ->
MessageRow(message)
}
}
LazyColumn {
items(books, key = { it.id }) {
val rememberedValue = remember {
Random.nextInt()
}
}
}
그러나 항목 키로 사용할 수 있는 유형에는 한 가지 제한이 있습니다. 키의 유형은 활동이 다시 생성될 때 상태를 유지하기 위한 안드로이드의 메커니즘인 번들에서 지원되어야 합니다. Bundle은 프리미티브, 열거형 또는 패커블과 같은 유형을 지원합니다.
LazyColumn {
items(books, key = {
// primitives, enums, Parcelable, etc.
}) {
// ...
}
}
LazyColumn {
items(books, key = { it.id }) {
val rememberedValue = rememberSaveable {
Random.nextInt()
}
}
}
Item animations
LazyColumn {
items(books, key = { it.id }) {
Row(Modifier.animateItemPlacement()) {
// ...
}
}
}
필요한 경우 사용자 지정 애니메이션 사양을 제공할 수도 있습니다:
LazyColumn {
items(books, key = { it.id }) {
Row(
Modifier.animateItemPlacement(
tween(durationMillis = 250)
)
) {
// ...
}
}
}
이동한 요소의 새 위치를 찾을 수 있도록 항목에 키를 제공해야 합니다.
Tips on using Lazy layouts
Avoid using 0-pixel sized items
@Composable
fun Item(imageUrl: String) {
AsyncImage(
model = rememberAsyncImagePainter(model = imageUrl),
modifier = Modifier.size(30.dp),
contentDescription = null
// ...
)
}
Avoid nesting components scrollable in the same direction
이는 같은 방향으로 스크롤 가능한 다른 부모 안에 미리 정의된 크기가 없는 스크롤 가능한 자식을 중첩하는 경우에만 적용됩니다. 예를 들어 세로로 스크롤 가능한 Column 부모 안에 높이가 고정되지 않은 자식 LazyColumn을 중첩하려고 할 때를 들 수 있습니다:
// throws IllegalStateException
Column(
modifier = Modifier.verticalScroll(state)
) {
LazyColumn {
// ...
}
}
LazyColumn {
item {
Header()
}
items(data) { item ->
PhotoItem(item)
}
item {
Footer()
}
}
Row(
modifier = Modifier.horizontalScroll(scrollState)
) {
LazyColumn {
// ...
}
}
Column(
modifier = Modifier.verticalScroll(scrollState)
) {
LazyColumn(
modifier = Modifier.height(200.dp)
) {
// ...
}
}
Beware of putting multiple elements in one item
LazyVerticalGrid(
columns = GridCells.Adaptive(100.dp)
) {
item { Item(0) }
item {
Item(1)
Item(2)
}
item { Item(3) }
// ...
}
레이지 레이아웃은 예상대로 이 문제를 처리합니다. 마치 다른 항목인 것처럼 요소를 차례로 배치합니다. 하지만 이렇게 하면 몇 가지 문제가 있습니다.
여러 요소가 하나의 항목의 일부로 표시되면 하나의 엔티티로 처리되므로 더 이상 개별적으로 구성할 수 없습니다. 하나의 요소가 화면에 표시되면 해당 항목에 해당하는 모든 요소를 구성하고 측정해야 합니다. 이는 과도하게 사용하면 성능이 저하될 수 있습니다. 극단적으로 모든 요소를 하나의 항목에 넣는 경우에는 레이지 레이아웃의 사용 목적이 완전히 무색해집니다. 잠재적인 성능 문제 외에도 한 항목에 더 많은 요소를 넣으면 scrollToItem() 및 animateScrollToItem()을 방해하게 됩니다.
하지만 목록 안에 디바이더를 넣는 것처럼 하나의 항목에 여러 요소를 넣는 유효한 사용 사례가 있습니다. 디바이더는 독립적인 요소로 간주되어서는 안 되므로 스크롤 인덱스가 변경되는 것을 원하지 않을 것입니다. 또한 디바이더는 크기가 작기 때문에 성능에 영향을 미치지 않습니다. 디바이더는 이전 항목의 일부가 될 수 있도록 이전 항목이 표시될 때 디바이더가 표시되어야 할 가능성이 높습니다:
LazyVerticalGrid(
columns = GridCells.Adaptive(100.dp)
) {
item { Item(0) }
item {
Item(1)
Divider()
}
item { Item(2) }
// ...
}
Consider using custom arrangements
일반적으로 레이지 목록에는 많은 항목이 있으며 스크롤 컨테이너 크기보다 더 많이 차지합니다. 그러나 목록에 항목이 적은 경우 뷰포트에서 항목을 배치하는 방법에 대해 보다 구체적인 요구 사항을 디자인에 반영할 수 있습니다.
이를 위해 사용자 지정 세로 배열을 사용하여 LazyColumn에 전달할 수 있습니다. 다음 예제에서 TopWithFooter 객체는 배열 메서드만 구현하면 됩니다. 첫째, 항목을 차례로 배치합니다. 둘째, 사용된 총 높이가 뷰포트 높이보다 낮으면 바닥글을 하단에 배치합니다:
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()
}
}
}
Consider adding contentType
LazyColumn {
items(elements, contentType = { it.type }) {
// ...
}
}
'Android > Compose Docs' 카테고리의 다른 글
Compose Docs | Understand gestures (0) | 2023.10.30 |
---|---|
Compose Docs | Animation modifiers and composables (0) | 2023.09.21 |
Compose Docs | Anatomy of a theme in Compose (0) | 2023.09.16 |
Compose Docs | Custom Design Systems (0) | 2023.09.16 |
Compose Docs | ConstraintLayout (0) | 2023.09.06 |