시배's Android

TiTi Side Project | LineChart 컴포넌트 구현하기 본문

Android/TiTi Side Project

TiTi Side Project | LineChart 컴포넌트 구현하기

si8ae 2024. 2. 7. 11:24

TdsTimeLineBar는 TiTI 프로젝트에서 사용되는 커스텀한 타임라인 막대 그래프입니다. 이 그래프는 사용자가 하루 동안의 공부 시간을 쉽게 시각화할 수 있도록 도와줍니다. 각 시간대에 할당된 공부 시간을 백분율로 나타내며, 막대 그래프의 배경으로 Gradient 컬러가 들어가고 있습니다.

@Composable
fun TdsTimeLineChart(
    modifier: Modifier = Modifier,
    times: List<Int>,
    startColor: Color,
    endColor: Color,
) {
    require(times.size == 24) {
        "The times list must be 24 in size"
    }

    BoxWithConstraints(modifier = modifier) {
        val itemWidth = maxWidth / times.size

        Row(modifier = Modifier.fillMaxSize()) {
            times.forEachIndexed { index, time ->
                TdsTimeLineBar(
                    modifier = Modifier
                        .width(itemWidth)
                        .fillMaxHeight(),
                    time = time,
                    hour = (index + 6).let { if (it >= 24) it - 24 else it }.toString(),
                    brush = Brush.verticalGradient(
                        listOf(
                            startColor,
                            endColor,
                        ),
                    ),
                )
            }
        }
    }
}

TdsTimeLineChart 컴포저는 24시간 동안의 데이터를 시각적으로 표현하는 타임라인 차트를 생성합니다. 각 시간은 times 매개변수를 통해 제공되며, 시작 색상(startColor)과 끝 색상(endColor)은 타임라인 차트의 그라데이션 배경을 정의합니다. BoxWithConstraints를 사용하여 부모 요소의 크기를 활용하며, 그 크기에 따라 각 시간 단위의 너비를 계산합니다. 그 후 Row를 사용하여 시간별로 각각의 TdsTimeLineBar 컴포넌트를 배치합니다.

@Composable
private fun TdsTimeLineBar(modifier: Modifier = Modifier, time: Int, hour: String, brush: Brush) {
    val textMeasurer = rememberTextMeasurer()
    val textStyle = TdsTextStyle
        .NORMAL_TEXT_STYLE
        .getTextStyle(fontSize = 10.sp)
        .copy(color = TdsColor.TEXT.getColor())
    val textLayoutResult = remember(hour) {
        textMeasurer.measure(hour, textStyle)
    }

    Canvas(modifier = modifier) {
        val spacing = 4.dp.toPx()

        val radius = size.width * 0.5f
        val barWidth = size.width * 0.9f
        val barMaxHeight = size.height - spacing - textLayoutResult.size.height
        val barHeight = barMaxHeight * time / 3600
        val barSpacing = size.width * 0.05f
        val startY = size.height - barHeight - spacing - textLayoutResult.size.height
        val cornerRadius = CornerRadius(radius, radius)
        val path = Path().apply {
            addRoundRect(
                RoundRect(
                    rect = Rect(
                        offset = Offset(
                            x = barSpacing,
                            y = startY,
                        ),
                        size = Size(barWidth, barHeight),
                    ),
                    topLeft = cornerRadius,
                    topRight = cornerRadius,
                ),
            )
        }

        drawPath(
            path = path,
            brush = brush,
        )

        drawText(
            textMeasurer = textMeasurer,
            text = hour,
            style = textStyle,
            topLeft = Offset(
                x = center.x - textLayoutResult.size.width / 2,
                y = size.height - textLayoutResult.size.height,
            ),
        )
    }
}
  1. 막대 그래프 모양과 위치 계산하기:
    • radius: 막대 그래프의 모양을 결정하는 원의 반지름입니다.
    • barWidth: 막대 그래프의 너비입니다.
    • barMaxHeight: 막대 그래프의 최대 높이로, 막대 그래프의 전체 높이에서 텍스트의 높이를 뺀 값입니다.
    • barHeight: 시간에 따른 막대 그래프의 높이입니다. 공부한 시간의 비율에 따라 계산됩니다.
    • barSpacing: 막대 그래프와 화면의 경계 사이의 간격입니다.
    • startY: 막대 그래프의 시작 Y 좌표입니다.
    • cornerRadius: 막대 그래프의 둥근 모서리를 정의하는 객체입니다.
  2. 막대 그래프 그리기:
    • drawPath: Canvas에 Path 객체를 사용하여 막대 그래프를 그립니다. Path는 여러 개의 선 및 곡선으로 이루어진 도형을 그릴 수 있도록 도와주는 객체입니다. 여기서는 둥근 사각형을 그리기 위해 addRoundRect 함수를 사용하였습니다.
  3. 시간 텍스트 그리기:
    • drawText: Canvas에 텍스트를 그립니다. 여기서는 시간을 나타내는 문자열을 그립니다. 텍스트의 위치는 Offset 객체를 통해 지정하며, 텍스트의 중앙을 기준으로 수평 정렬됩니다.

 

종합하여 주어진 time 매개변수에 따라 바의 높이를 계산하고, 시간을 나타내는 텍스트를 추가합니다. Canvas 내에서는 시간별 바의 형태와 위치를 계산하여 그리고, 그라데이션으로 색칠된 바를 표시합니다. 마지막으로 시간 텍스트를 바의 하단에 표시합니다.