グラフライブラリ 3. グラフデータの作成

*この記事は韓国語から翻訳したものです。不十分な部分があれば、いつでもフィードバックをいただければありがたいです！ ([オリジナル記事](https://blog.chungjungsoo.dev/dev-posts/graph-library-3/), 同じく私が作成しました。)*


グループプロジェクトでグラフライブラリを実装する過程をまとめてみました。今回の記事ではその中でグラフデータを描画する部分について説明します。([project repo](https://github.com/boostcampwm2023/and09-PriceGuard), [library repo](https://github.com/Taewan-P/material-android-chart))


## グラフデザインモチーフ

まず、実装方法を説明する前になぜこのライブラリを実装することになったのか説明します。

ブーストキャンプグループのプロジェクトデザインをする時、Material Designを遵守するデザインで制作しようと思い、グラフのデザインもMaterial的に反映させようと思いました。


![initial_design.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3896189/5d857c8e-ad15-bb5c-8b5b-d29d66fcc05f.png)
「初期デザイン」


![graph_motive.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3896189/f85f09dd-ed1d-8159-d605-30c170577b61.png)
「グラフのモチーフ」

グラフデザインのインスピレーションはよく使ってるGoogle Finance Widgetから持ってきました。このウィジェットの株式グラフが本当にMaterial的なデザインだと思い、初期デザインに反映しました。

Androidグラフライブラリの中で一番有名なのは[MPAndroidChart](https://github.com/PhilJay/MPAndroidChart)です。最初に調査をする時、このライブラリは**使わない**ことにしました。その理由は**19年以降リリースがない**メンテナンスがされていないライブラリであり、また、プロジェクトをデザインした通りにデザインをMaterial的にカスタマイズすることが難しいと思いました。私たちのプロジェクトに合うようにデザインも合わせ、機能も合わせ、グラフライブラリを直接実装することが技術的な挑戦になると判断し、ライブラリを直接実装することにしました。


## 実装方法


### グラデーションをつける

Google Finance Widgetのグラデーションをよく見ると、グラフの線の形とは関係なく、高いところはグラデーションの程度が同じで、低いところはグラデーションが同じに仕上げられています。 つまり、y軸方向に平行になるようにグラデーションが進行します。したがって、グラデーションを先にグラフの背景として描き、グラフで必要ない上部のグラデーションは背景色で塗りつぶし、グラフを描く方法で実装しようとします。

```kotlin
val chartSpaceEndY =
    Px(height.toFloat()) - yAxisMarginStart.toPx(context) - Px(axisStrokeWidth)

// Set gradation position, color, mode
val gradationEndY = Px(height.toFloat()) - yAxisMarginStart.toPx(context)

gradientPaint.shader =
    LinearGradient(
        graphSpaceStartX.value,
        graphSpaceStartY.value,
        graphSpaceStartX.value,
        gradationEndY.value,
        ColorUtils.setAlphaComponent(colorPrimary, 180),
        Color.TRANSPARENT,
        TileMode.CLAMP
    )

// Fill gradation
canvas.drawRect(
    graphSpaceStartX.value,
    graphSpaceStartY.value + 1f,
    graphSpaceEndX.value,
    chartSpaceEndY.value,
    gradientPaint
)
```

### 線の外側のグラデーションを消す

グラデーションを消す部分は背景色で塗りつぶす部分です。グラフの各データを使って位置値を計算するコードがありますが、これは線の作成でも同じように使います。各グラフの線が描画される部分の上部を塗りつぶす方法で実装しました。



```kotlin
gradientCoverPaint.setGradientPaint()

chartData.forEachIndexed { index, data ->
    if (index < size - 1) {
        val next = chartData[index + 1]

        // Calculate position of each data
        val startX = Px((data.x - minX) / spaceX) * graphWidth + graphSpaceStartX
        val startY = Px(1 - (data.y - minY) / spaceY) * graphHeight + graphSpaceStartY
        val endX = Px((next.x - minX) / spaceX) * graphWidth + graphSpaceStartX

        // Hide the area that doesn't require a gradation
        canvas.drawRect(
            startX.value - 1F,
            0F,
            endX.value + 1F,
            startY.value,
            gradientCoverPaint
        )
    }
}
```

### 線作成

最後に線の作成です。グラデーションを消す部分と同じように位置値を計算した後、その部分に合わせて線を描けばよい。価格が同じであればx軸と平行に、価格が上がる場合は上がる前の値から直角に線をつなげず、上がった時点から直角に上がるように作成しました。


```kotlin
linesPaint.setLinePaint()

chartData.forEachIndexed { index, data ->
    if (index < size - 1) {
        val next = chartData[index + 1]

        // Calculate position of each data
        val startX = Px((data.x - minX) / spaceX) * graphWidth + graphSpaceStartX
        val startY = Px(1 - (data.y - minY) / spaceY) * graphHeight + graphSpaceStartY
        val endX = Px((next.x - minX) / spaceX) * graphWidth + graphSpaceStartX
        val endY = Px(1 - (next.y - minY) / spaceY) * graphHeight + graphSpaceStartY

        canvas.drawLine(startX.value, startY.value, endX.value, startY.value, linesPaint)
        canvas.drawLine(endX.value, startY.value, endX.value, endY.value, linesPaint)
    }
}
```

次の記事では、グラフにデザイン/テーマカラーを適用することについて説明します。