문제 상황
Jetpack Compose에서 LazyRow를 사용하다 보면 각 아이템의 높이에 따라 LazyRow의 전체 높이가 변경되는 상황을 종종 겪게 됩니다.
특히 아래 스크린샷 같이, 제목이 한 줄 또는 두 줄로 구성되어 있을 때, 두 줄인 항목의 높이에 맞춰서 LazyRow가 고정될 것이라고 예상합니다.
하지만 사용자가 스크롤하여 해당 항목이 화면에서 사라지면 LazyRow의 높이가 줄어드는 현상이 발생합니다.
해결 방안
이를 해결하기 위해선 LazyRow의 전체 높이를 아이템 중 가장 큰 높이에 맞추어야 합니다.
가장 큰 높이의 항목을 기준으로 LazyRow의 높이를 고정하기 위해 SubComposeLayout을 이용합니다.
SubComposableLayout은 레이아웃을 그리는 과정에서 다른 레이아웃의 크기가 필요한 경우 유용하게 사용할 수 있습니다.
MeasuredHeightContainer
아래는 SubComposableLayout을 활용한 MeasuredHeightContainer입니다.
이 컴포저블은 LazyRow와 함께 사용될 수 있으며, measured 컴포저블을 통해 가장 큰 높이를 결정하고 이를 기준으로 LazyRow의 높이를 고정합니다.
/**
* @property Measured 높이를 측정하기 위한 슬록
* @property Content 측정한 높이를 사용할 콘텐츠용 슬롯
*/
enum class SlotId {
Measured,
Content
}
/**
* LazyRow의 높이를 아이템의 최대 높이로 고정하기 위한 컴포저블
*
* @param measured 측정하고 싶은 높이를 갖는 Composable
* @param content 측정한 높이를 높이로 갖는 Composable
*/
@Composable
fun MeasuredHeightContainer(
modifier: Modifier = Modifier,
measured: @Composable () -> Unit,
content: @Composable () -> Unit,
) {
SubcomposeLayout(modifier = modifier) { constraints ->
// 최소 너비 0으로 설정
val wrappedConstraints = constraints.copy(minWidth = 0)
// measured 컴포저블을 한 번만 서브 컴포지션(subcompose)하고 이를 측정
// 여기서 얻은 높이를 이후 content 컴포저블의 고정 높이로 사용
val measuredHeight = subcompose(SlotId.Measured, measured).first().measure(wrappedConstraints).height
val fixedHeightConstraints = wrappedConstraints.copy(
minHeight = measuredHeight,
maxHeight = measuredHeight
)
// content 컴포저블을 측정하여 placeables 리스트에 저장
val contentPlaceables: List<Placeable> = subcompose(SlotId.Content, content).map {
it.measure(fixedHeightConstraints)
}
// 레이아웃 너비 계산
// 고정된 너비로 설정된 경우 그 값을 사용, 그렇지 않으면 자식 콘텐츠들의 너비 합산
val layoutWidth = when {
constraints.hasFixedWidth -> constraints.maxWidth // 고정 너비인 경우
constraints.hasBoundedWidth -> constraints.maxWidth.coerceAtMost(contentPlaceables.sumOf { it.width }) // 범위로 설정된 경우
else -> contentPlaceables.sumOf { it.width }.coerceIn(constraints.minWidth, constraints.maxWidth)
}
// 콘텐츠를 가로로 배치
layout(layoutWidth, measuredHeight) {
var xPos = 0
contentPlaceables.forEach { placeable ->
placeable.placeRelative(xPos, 0)
xPos += placeable.width
}
}
}
}
사용 방법
@Composable
fun RecommendPlaylistLayout(playlists: List<RecommendPlaylist>) {
Column {
OneLineTitle(title = "위플리 추천 플레이리스트")
MeasuredHeightContainer(
modifier = Modifier,
measured = {
PlayListCoverItem(recommendPlaylist = playlists.maxBy { it.title.length })
},
) {
LazyRow(
horizontalArrangement = Arrangement.spacedBy(12.dp),
contentPadding = PaddingValues(horizontal = 20.dp)
) {
items(playlists) { playlist ->
PlayListCoverItem(playlist)
}
}
}
}
}
'안드로이드 > Compose' 카테고리의 다른 글
[Compose] - State가 변경 되어도 UI에 반영이 안되는 이슈 해결 (0) | 2024.10.07 |
---|---|
[Compose] - Design System 구축 - 2. ColorScheme 만들기 (0) | 2024.08.04 |
[Compose] - Design System 구축 - 1. Typography 만들기 (0) | 2024.08.02 |
[ Compose ] - Compose Box (0) | 2024.05.19 |