시배's Android

Compose | Compose LazyColumn에서 FadingEdge 구현하기 본문

Android/Compose

Compose | Compose LazyColumn에서 FadingEdge 구현하기

si8ae 2023. 7. 26. 23:05
fun Modifier.topFadingEdge(
    lazyListState : LazyListState,
    length : Dp,
    edgeColor : Color? = null
) = composed(
    debugInspectorInfo {
        name = "length"
        value = length
    }
){
    val color = edgeColor ?: MaterialTheme.colorScheme.surface

    drawWithContent {
        val topFadingEdgeStrength by derivedStateOf {
            lazyListState.layoutInfo.run {
                val firstItem = visibleItemsInfo.first()
                when {
                    visibleItemsInfo.size in 0..1 -> 0f
                    firstItem.index > 0 -> 1f
                    firstItem.offset == viewportStartOffset -> 0f
                    firstItem.offset < viewportStartOffset -> firstItem.run {
                        abs(offset) / size.toFloat()
                    }
                    else -> 1f
                }
            }.coerceAtMost(1f) * length.value
        }

        drawContent()

        drawRect(
            brush = Brush.verticalGradient(
                colors = listOf(
                    color,
                    Color.Transparent,
                ),
                startY = 0f,
                endY = topFadingEdgeStrength,
            ),
            size = Size(
                this.size.width,
                topFadingEdgeStrength
            ),
        )
    }
}
  • drawWithContent: 기존의 컨텐츠와 함께 Fading Edge를 그리는 블록을 정의합니다.
  • derivedStateOf: lazyListState를 기반으로 계산된 Top Fading Edge의 강도를 추출합니다.
  • lazyListState.layoutInfo: LazyListState에 포함된 layoutInfo 속성으로부터 RecyclerView의 레이아웃 정보를 가져옵니다.
    • visibleItemsInfo.first() : 현재 화면에 보이는 첫 번째 아이템의 정보를 가져옵니다.
    • visibleItemsInfo.size in 0..1 : 현재 화면에 보이는 아이템이 0개 또는 1개일 경우, 즉 스크롤이 최상단에 위치할 경우 topFadingEdgeStrength는 0이 됩니다. (최상단에 아이템이 없거나 하나인 경우에는 페이딩 효과가 필요하지 않기 때문입니다.)
    • firstItem.index > 0 : 첫 번째 아이템이 최상단이 아닌 경우, 즉 스크롤이 최상단에서 멀어졌을 경우 topFadingEdgeStrength는 1이 됩니다. (최상단에서 멀어질수록 페이딩 효과가 강해집니다.)
    • firstItem.offset == viewportStartOffset : 첫 번째 아이템이 정확히 최상단에 위치한 경우, topFadingEdgeStrength는 0이 됩니다. (첫 번째 아이템이 정확히 최상단에 위치할 때도 페이딩 효과가 필요하지 않기 때문입니다.)
    • firstItem.offset < viewportStartOffset : 번째 아이템이 최상단보다 위에 위치한 경우, 스크롤이 최상단에서 멀어진 경우 topFadingEdgeStrength abs(offset) / size.toFloat() 통해 계산됩니다. 값은 스크롤이 최상단에서 멀어질수록 커집니다. (offset 번째 아이템이 최상단에서 멀어진 거리를 의미하며, size 첫번 째 아이템 전체 사이즈를 의미합니다.)
    • 이렇게 계산된 topFadingEdgeStrength 값은 0부터 1까지의 범위를 갖기 때문에, length라는 Fading Edge 길이와 곱해줌으로써 실제 Fading Edge 높이를 결정합니다.
  • drawContent: 기존의 컨텐츠를 그립니다.
  • drawRect: Fading Edge 그립니다. Brush.verticalGradient 사용하여 그라데이션 효과를 적용하며, startY endY 이용하여 Fading Edge 강도를 지정합니다.