Android 개발에서 Jetpack Compose 채택이 늘어감에 따라 주요 컴포넌트 중 하나인 LazyColumn/LazyRow에 대해서 알아보겠습니다. 앞으로 나오는 코드는 public 및 내부 코드를 기반으로 파악한 부분입니다.
테스트에 사용한 버전 : androidx.compose:compose-bom-alpha:2024.12.01
LazyColumn/LazyRow 구조
기본적으로 LazyColumn/LazyRow는 현재 표시할 Item만 배치하는 세로/가로형 스크롤 가능한 리스트입니다. 기존 Recyclerview의 Compose 버전에 해당하는 Composable이며, 이름에서도 알 수 있지만 Recyclerview과도 동일하게 재사용, 필요한 항목 노출, 지연 호출 등이 특징인 Composable입니다.
LazyColumn/LazyRow 둘 다 내부적으로 internal LazyList를 호출합니다. 2개의 차이점은 reverseLayout 파라미터에 따른 항목을 그리는 방향만 다르다라는 점입니다.
@Composable
fun LazyColumn(
...,
state: LazyListState = rememberLazyListState(),
...
reverseLayout: Boolean = false,
verticalArrangement: Arrangement.Vertical =
if (!reverseLayout) Arrangement.Top else Arrangement.Bottom,
horizontalAlignment: Alignment.Horizontal = Alignment.Start,
...
) {
LazyList(
...,
isVertical = true,
...
)
}
@Composable
fun LazyRow(
...,
state: LazyListState = rememberLazyListState(),
...
reverseLayout: Boolean = false,
horizontalArrangement: Arrangement.Horizontal =
if (!reverseLayout) Arrangement.Start else Arrangement.End,
verticalAlignment: Alignment.Vertical = Alignment.Top,
...
) {
LazyList(
...,
isVertical = false,
...
)
}
rememberLazyListState는 컴포지션 전체에서 기억되는 LazyListState를 생성합니다. 가장 먼저 표시되는 항목의 인덱스와 그 항목의 스크롤 오프셋을 초기값으로 지정할 수 있습니다. 아래 소스에서도 알 수 있듯이 초기값은 0입니다.
@Composable
fun rememberLazyListState(
initialFirstVisibleItemIndex: Int = 0,
initialFirstVisibleItemScrollOffset: Int = 0
): LazyListState {
return rememberSaveable(saver = LazyListState.Saver) {
LazyListState(initialFirstVisibleItemIndex, initialFirstVisibleItemScrollOffset)
}
}
@Composable
fun rememberLazyListState(
initialFirstVisibleItemIndex: Int = 0,
initialFirstVisibleItemScrollOffset: Int = 0,
prefetchStrategy: LazyListPrefetchStrategy = remember { LazyListPrefetchStrategy() },
): LazyListState {
return rememberSaveable(prefetchStrategy, saver = LazyListState.saver(prefetchStrategy)) {
LazyListState(
initialFirstVisibleItemIndex,
initialFirstVisibleItemScrollOffset,
prefetchStrategy
)
}
}
LazyListState는 스크롤을 제어하고 관찰하기 위해 호이스팅 상태 객체입니다. 대부분은 rememberLazyListState를 통해 생성됩니다.
class LazyListState : ScrollableState
LazyListState 객체는 ScrollableState 인터페이스의 구현체이기도 합니다. 즉, ScrollableState 인터페이스를 파라미터로 전달받는 곳이라면 LazyListState 전달도 가능합니다.
스크롤할 수 있는 것을 나타내는 객체 인터페이스입니다. 또는 와 같은 스크롤 가능한 컨테이너의 상태로 구현됩니다.
ScrollableState 인터페이스의 대표적인 구현체로는 아래와 같습니다.
LazyListPrefetchStrategy 인터페이스의 구현은 사용자가 LazyList와 상호 작용할 때 어떤 인덱스를 미리 가져올지(유휴 시간 동안 미리 구성하고 미리 측정할지)를 제어합니다. 구현은 onScroll
및 onVisibleItemsUpdated
콜백에서 LazyListPrefetchScope.schedulePrefetch
를 호출하여 사전 로드를 예약합니다.
interface LazyListPrefetchStrategy {
// 프리패치 요청을 실행하는 데 사용할 PrefetchScheduler
val prefetchScheduler: PrefetchScheduler?
get() = null
// 표시된 항목의 변경 여부와 관계없이 LazyList가 스크롤될 때 호출
fun LazyListPrefetchScope.onScroll(delta: Float, layoutInfo: LazyListLayoutInfo)
// LazyList가 스크롤될 때 보이는 항목이 변경되면 호출
fun LazyListPrefetchScope.onVisibleItemsUpdated(layoutInfo: LazyListLayoutInfo)
// 부모 LazyLayout이 이 LazyList를 포함하는 콘텐츠를 프리패치했을 때 onNestedPrefetch가 호출
// LazyList는 화면에 나타나기 전에 자식에 대한 프리패치를 요청할 수 있음
//
// onNestedPrefetch는 실제로 표시될 것으로 예상되는 자식들에 대해서만
// 목록을 스크롤하여 볼 때 실제로 표시될 것으로 예상되는 자식에 대해서만 프리패치를 요청해야 함
fun NestedPrefetchScope.onNestedPrefetch(firstVisibleItemIndex: Int)
}
// 프리패치를 허용하는 LazyListPrefetchStrategy의 콜백 범위
interface LazyListPrefetchScope {
// 지정된 인덱스에 대한 프리패치를 예약
// 요청은 요청된 순서대로 실행
// 요청된 프리패치가 더 이상 필요하지 않은 경우 (예: 스크롤 방향 변경으로 인해) 'LazyLayoutPrefetchState.PrefetchHandle.cancel'를 통해 요청을 취소
fun schedulePrefetch(index: Int): LazyLayoutPrefetchState.PrefetchHandle
}
LazyList에서는 rememberLazyListState Composable 호출 시 별도 LazyListPrefetchStrategy 지정이 없더라도 기본 LazyListPrefetchStrategy를 제공하고 있습니다.
// LazyListState.kt
@Composable
fun rememberLazyListState(
...,
prefetchStrategy: LazyListPrefetchStrategy = remember { LazyListPrefetchStrategy() },
): LazyListState {
...
}
// LazyListPrefetchStrategy.kt
// LazyList가 다른 LazyLayout 안에 중첩되어 있을 때,
// 몇 개의 내부 항목을 사전 로드해야 하는지를 지정하며 기본 값은 2입니다.
fun LazyListPrefetchStrategy(nestedPrefetchItemCount: Int = 2): LazyListPrefetchStrategy =
DefaultLazyListPrefetchStrategy(nestedPrefetchItemCount)
comments powered by Disqus
Subscribe to this blog via RSS.