Today's

길을 나서지 않으면 그 길에서 만날 수 있는 사람을 만날 수 없다

모바일 앱(안드로이드)

앱 에서 챠트 그려보기 도전 이야기... vico

Billcorea 2024. 7. 11. 15:34
반응형
0. 들어가는 글

 

https://billcorea.tistory.com/384

 

안드로이드 앱 만들기 : 앱에서 챠트 그래프 그리는 도구 (인터넷 펌)

https://github.com/patrykandpatrick/vico GitHub - patrykandpatrick/vico: A light and extensible chart library for Android. A light and extensible chart library for Android. Contribute to patrykandpatrick/vico development by creating an account on GitHub. g

billcorea.tistory.com

 

이전 글에서 vico 라이브러리를 활용한 chart 그리는 정보에 대한 이야기를 적어 보았던 기억이 납니다.   이번에는 관련 정보를 이용해 앱 개발에 적용해 본 이야기를 적어 볼까 합니다. 

 

앱 개발은 크몽에서 앱 개발 의뢰을 받아 도전해 보았습니다.  고객은 앱에 라인 차트가 들어가는 화면을 구현하기를 요청하였습니다.  다른 library 들도 있기는 하겠지만, 이번에 찾아보았던 것을 이용해 보기로 했습니다. 

 

또한, vico chart 에는 line chart 을 지원하기도 해서 도전해 보기로 했습니다. 

 

1. 개발자 페이지...

 

https://patrykandpatrick.com/vico/wiki/getting-started

 

Getting started | Vico

<iframe

patrykandpatrick.com

개발자 페이지에 나와 있는 wiki 정보는 해당 library 을 활용하는 데 도움이 됩니다.  끝까지 다 읽어 보면 좋기는 하겠지만,  긴급(?) 하게 작업을 해야 하는 경우에는 꼭 그럴 필요보다는 요약된 정보(?)를 활용해 보시는 것이 도움이 될 수 있습니다. 

 

 

2. build gradle 설정 
dependencies {
    // For Jetpack Compose.
    implementation("com.patrykandpatrick.vico:compose:2.0.0-alpha.22")

    // For `compose`. Creates a `ChartStyle` based on an M2 Material Theme.
    implementation("com.patrykandpatrick.vico:compose-m2:2.0.0-alpha.22")

    // For `compose`. Creates a `ChartStyle` based on an M3 Material Theme.
    implementation("com.patrykandpatrick.vico:compose-m3:2.0.0-alpha.22")

    // Houses the core logic for charts and other elements. Included in all other modules.
    implementation("com.patrykandpatrick.vico:core:2.0.0-alpha.22")

    // For the view system.
    implementation("com.patrykandpatrick.vico:views:2.0.0-alpha.22")
}

앱 수준의 gradle 파일에는 이런 정도의 기술이 필요 합니다. android studio 가 korala 버전으로 넘어가고 gradle 버전 8.5 이상으로 넘어가면서 version 관리를 version catalog을 사용하는 방식으로 변경이 되면서 작성하는 방법이 달라지고 있습니다.  처음 보았을 때는 불편하지 않을까 하는 생각이 들기도 했지만, 익숙해지면 관리가 수월해집니다. 

 

이번에는 version catalog 방식의 gradle 정보을 보겠습니다. 

먼저 grable 폴더에 위치 하는 libs.versions.toml 파일의 작성 예시입니다.  버전은 이 글을 작성하는 시점이기 때문에 달라질 수 있습니다.

[versions]
vico = "2.0.0-alpha.22"

[libraries]
# For Jetpack Compose.
vico-compose = { group = "com.patrykandpatrick.vico", name = "compose", version.ref = "vico" }

# For `compose`. Creates a `ChartStyle` based on an M2 Material Theme.
vico-compose-m2 = { group = "com.patrykandpatrick.vico", name = "compose-m2", version.ref = "vico" }

# For `compose`. Creates a `ChartStyle` based on an M3 Material Theme.
vico-compose-m3 = { group = "com.patrykandpatrick.vico", name = "compose-m3", version.ref = "vico" }

# Houses the core logic for charts and other elements. Included in all other modules.
vico-core = { group = "com.patrykandpatrick.vico", name = "core", version.ref = "vico" }

# For the view system.
vico-views = { group = "com.patrykandpatrick.vico", name = "views", version.ref = "vico" }

 

다음은 gradle 파일의 모습 입니다. 

dependencies {
    implementation(libs.vico.compose)
    implementation(libs.vico.compose.m2)
    implementation(libs.vico.compose.m3)
    implementation(libs.vico.core)
    implementation(libs.vico.views)
}

 

dependencies 작성이 휠씬 간결해졌음을 알 수 있습니다. 

 

이제 코드 구현을 해 보겠습니다. 

 

// compolse 에서 구현 되는 챠트 입니다.
@Composable
fun LineGraph() {
    val modelProducer = remember { CartesianChartModelProducer.build() }
    // 이 부분에서는 반복적으로 무의미한 숫자을 생성해 냅니다. 
    // 아직 데이터가 없는 그래프 이기 때문에 임시 데이터을 생성해 내기 위해서 입니다.
    LaunchedEffect(Unit) {
        withContext(Dispatchers.Default) {
            while (isActive) {
                modelProducer.tryRunTransaction {
                    /* Learn more:
                    https://patrykandpatrick.com/vico/wiki/cartesian-charts/lay
                    ers/line-layer#data. */
                    // 라인 챠트을 그리기 위해서 준비 합니다.
                    lineSeries {
                        repeat(Defaults.MULTI_SERIES_COUNT) {
                            series(
                                List(Defaults.ENTRY_COUNT) {
                                    Defaults.COLUMN_LAYER_MIN_Y +
                                            Random.nextFloat() * Defaults.COLUMN_LAYER_RELATIVE_MAX_Y
                                }
                            )
                        }
                    }
                }
                // 이 부분을 풀면 반복 시간이 되었을 떄 다시 실행 됩니다.
                //delay(Defaults.TRANSACTION_INTERVAL_MS)
            }
        }
    }

    // 이제 챠트을 그려 보겠습니다.
    Column (
        modifier = Modifier
            .fillMaxWidth(),
        horizontalAlignment = CenterHorizontally,
        verticalArrangement = Arrangement.Top
    ) {
        Spacer(modifier = Modifier.padding(10.dp))
        // 챠트 상단에 제목을 넣었습니다.
        Text(text = stringResource(R.string.statusTitle), style = typography.titleSmall.copy(white))
        Spacer(modifier = Modifier.padding(5.dp))
        // 챠트 그리기을 호출 합니다.
        ComposeChart7(modelProducer, Modifier.fillMaxSize())
    }

}

@Composable
private fun ComposeChart7(modelProducer: CartesianChartModelProducer, modifier: Modifier) {
    CartesianChartHost(
        chart =
        rememberCartesianChart(
            rememberLineCartesianLayer(
            // 챠트가 그려질 데이터 정보입니다.
                lines =
                chartColors.map { color ->
                    rememberLineSpec(shader = DynamicShader.color(color), backgroundShader = null)
                }
            ),
            // 챠트 하단 에 표시 되는 정보 입니다.
            startAxis =
            rememberStartAxis(
                label = rememberStartAxisLabel(),
                horizontalLabelPosition = VerticalAxis.HorizontalLabelPosition.Inside,
                // 하단 제목의 수직 배열시 참조 VerticalAxis.VerticalLabelPosition.Center
            ),
            // 챠트 왼쪽에 표시 되는 정보 입니다.
            bottomAxis = rememberBottomAxis(valueFormatter = { value, _,  _ ->
                ("${value.toInt()}H")
            }),
            legend = rememberLegend(),
        ),
        modelProducer = modelProducer,
        modifier = modifier.background(white),
        marker = rememberMarker(),
        runInitialAnimation = false,
        zoomState = rememberVicoZoomState(zoomEnabled = false),
    )
}

// 챠트 왼쪽에 제목 (세로 제목)
@Composable
private fun rememberStartAxisLabel() =
    rememberAxisLabelComponent(
        color = white,
        background = rememberShapeComponent(shape = Shape.rounded(4.dp), color = softBlue),
        padding = Dimensions.of(horizontal = 8.dp, vertical = 2.dp),
        margins = Dimensions.of(all = 4.dp),
    )

// 챠트가 그려지는 부분 입니다.
@Composable
private fun rememberLegend() =
    rememberHorizontalLegend<CartesianMeasureContext, CartesianDrawContext>(
        items =
        chartColors.mapIndexed { index, chartColor ->
            rememberLegendItem(
                icon = rememberShapeComponent(Shape.Pill, chartColor),
                label =
                rememberTextComponent(
                    color = vicoTheme.textColor,
                    textSize = 12.sp,
                    typeface = Typeface.MONOSPACE,
                ),
                labelText = stringResource(legendString[index]),
            )
        },
        iconSize = 8.dp,
        iconPadding = 8.dp,
        spacing = 4.dp,
        padding = Dimensions.of(top = 8.dp),
    )

// Color(0xffb983ff), Color(0xff91b1fd), Color(0xff8fdaff)
// 챠트는 3개의 데이터을 이용해 그리는 것이라 구분 하기 위해서 color 을 선언 합니다.
private val chartColors = listOf(smallGreen, smallYellow, largeGreen)
// 챠트 하단에 표시될 제목 입니다. 데이터가 3개 이기 때문에 제목도 3개을 넣었습니다.
private val legendString = listOf(R.string.walkStep,R.string.calorieBurn,R.string.exerciseTime)

 

이렇게 간단한 설명을 달아 보았습니다.   개발자 페이지를 잘 보면 상세한 정보를 보실 수 있을 듯합니다만, 속성(?)으로 차트 그리기가 필요하다면, 이 정도의 정보만으로도 멋있는 line chart을 그려 볼 수 있습니다. 

 

4. 실행 예제

 

chart 예제

 

이렇게 그려지는 chart 을 보실 수 있습니다.  실행되는 앱의 전체 화면은 고객 보호를 위해서 이 부분만 참조하도록 하겠습니다.

반응형