[Compose] Modifier Extension - DrawScrollbar

drawScrollbar 함수는 Modifier 클래스에 대한 확장 함수로, LazyColumn에 커스텀 스크롤바를 그릴 수 있게 해줍니다.

이 함수는 drawBehind를 사용하여 컴포저블의 캔버스에 직접 스크롤바를 그립니다.

 

해당 함수는 아래 이슈를 참고하였습니다.

 

Google Issue Tracker

 

issuetracker.google.com

 

/**
 * LazyColumn을 위한 사용자 정의 스크롤바를 그리기 위한 확장 함수
 *
 * @param state LazyListState로, lazy list의 상태를 포함
 * @param barColor 스크롤바의 색상
 * @param barWidth 스크롤바의 너비
 * @param barBottomPadding 스크롤바의 하단 패딩
 */
 
fun Modifier.drawScrollbar(
    state: LazyListState,
    barColor: Color = Color.Gray,
    barWidth: Dp = 4.dp,
    barBottomPadding: Dp = 0.dp
) = drawBehind {
    val firstVisibleElementIndex = state.layoutInfo.visibleItemsInfo.firstOrNull()?.index
    val lastVisibleElementIndex = state.layoutInfo.visibleItemsInfo.lastOrNull()?.index

    val needScrollbar = state.layoutInfo.totalItemsCount > (lastVisibleElementIndex ?: 0)

    if (needScrollbar) {
        val elementHeight = (this.size.height - barBottomPadding.toPx()) / state.layoutInfo.totalItemsCount
        val scrollbarOffsetY = firstVisibleElementIndex!! * elementHeight
        val scrollbarHeight = (lastVisibleElementIndex!! - firstVisibleElementIndex + 1) * elementHeight

        drawRoundRect(
            color = barColor,
            topLeft = Offset(this.size.width - barWidth.toPx(), scrollbarOffsetY),
            size = Size(barWidth.toPx(), scrollbarHeight),
            cornerRadius = CornerRadius(barWidth.toPx() / 2)
        )
    }
}

 

파라미터 설명

  • state: LazyListState: LazyColumn의 상태를 포함하며, 현재 보이는 아이템 정보 등을 가지고 있습니다.
  • barColor: Color: 스크롤바의 색상을 지정합니다.
  • barWidth: Dp: 스크롤바의 너비를 지정합니다.
  • barBottomPadding: Dp: 스크롤바의 하단 패딩을 설정할 수 있습니다.

 

동작 방식

  1. 스크롤바 필요 여부 판단:
    스크롤바는 리스트의 총 아이템 개수가 마지막 보이는 아이템보다 많을 때에만 그려집니다.
    즉, 스크롤이 필요한 경우에만 스크롤바가 나타납니다.

  2. 스크롤바 위치 계산:
    • 스크롤바에서 각 아이템의 높이는 전체 리스트 아이템 수에 따라 나누어 계산됩니다.
    • 첫 번째로 보이는 아이템의 인덱스를 기준으로 스크롤바의 Y축 오프셋이 계산됩니다.
  3. 스크롤바 그리기:
    계산된 높이와 너비로 오른쪽 끝에 맞춰 둥근 직사각형 형태의 스크롤바가 그려집니다.

 

사용 예시

다음과 같이 LazyColumndrawScrollbar 함수를 적용할 수 있습니다.

val listState = rememberLazyListState()

LazyColumn(
    modifier = Modifier
        .fillMaxSize()
        .drawScrollbar(state = listState),
    state = listState
) {
    items(100) { index ->
        Text("Item $index", Modifier.padding(16.dp))
    }
}

위 예시에서는 100개의 아이템이 포함된 LazyColumn에 커스텀 스크롤바가 추가됩니다.
스크롤바의 색상, 너비, 하단 패딩 등을 필요에 맞게 변경할 수 있습니다.

 

 

아쉬운 점

스크롤할 때 스크롤바가 자연스럽지 않게 그려지는 문제가 있습니다.

짧아졌다 길어졌다하는 문제가 있습니다.

 

추후 더 자연스럽게 그려지도록 개선하여 추가로 작성할 예정입니다.