グラフライブラリ 1. カスタムビュー生成

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


グループプロジェクトでグラフライブラリを実装する過程をまとめてみました。この記事ではプロジェクトの初期設定やカスタムビューを作る過程について説明します。([project repo](https://github.com/boostcampwm2023/and09-PriceGuard), [library repo](https://github.com/Taewan-P/material-android-chart))


## プロジェクト初期設定


### プロジェクト構造

```
Project Root
│
├───app
│   ├───src
│   └───build.gradle.kts
│
├───materialchart
│   ├───src
│   └───build.gradle.kts
│
├───build.gradle.kts
├───gradlew
├───gradle.properties
└───などなど色んなファイル...

```


色んなアンドロイドオープンソースライブラリを参考した結果、ほとんどのライブラリが上記のような構造を持っていました。ライブラリを実装するプロジェクトは `library name` フォルダに、ライブラリを使うサンプルアプリのソースコードは `app` フォルダに配置するのが一般的なようです。ライブラリを開発した後、テストする時にもサンプルアプリで簡単にテストすることができるし、オープンソースライブラリ配布が目標という二つの理由から上記の構造でプロジェクトを構成しました。

### 配布前に使いたい時

上のようにプロジェクトを構成すると、一つの問題が発生します。ライブラリをMavenのようなリモートリポジトリにデプロイすれば問題はないですが、現在の状態でグラフライブラリを使おうとする元のプロジェクトで使おうとすると、1.ライブラリのソースだけコピー＆ペーストしてテスト、2.サブモジュールで追加する二つの方法があります。

1番の方法はライブラリを修正する時、毎回コピペしなければならない不便さがありますし、2番の方法はサブモジュールを使う方法なので、レポ全体のコードが全部モジュールフォルダの中に入るので、モジュール内の構造も合わないし、サンプルアプリのソースまで入るので問題が発生します。


![chart_library_structure.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3896189/14e8ca1a-62cf-0c6c-2016-8afe7d131f04.png)
「subtreeを活用したリポジトリの構造」


この問題を解決するためsubtreeを活用しました。Git Subtreeはリポジトリのsubdirectoryをコピーすることができる機能です。この機能を使ってライブラリのソースコードだけ入ってるライブラリフォルダをsubtreeにコピーして他のブランチに入れました。Subtreeにコピーされたブランチを確認してみると、ライブラリのソースコードがrootフォルダにあるファイル構造が見えます。このように設定した後、ライブラリを使いたい元のプロジェクトにはそのブランチに従うサブモジュールを追加すれば終わりです。

```shell
git submodule add -b <subtree_branch> https://github.com/Taewan-P/material-android-chart <your_module_folder_location>
```

完璧に見えますが、まだ足りないです。ライブラリを修正したからといって、subtreeで作ったブランチのファイルが変わるわけではありません。 そのため、ライブラリを修正して、その変更をsubtreeにコピーされたブランチに反映する必要があります。そのためには `git subtree push` コマンドを使います。

```shell
git subtree push --prefix materialchart origin <subtree_branch>
```

毎回pushするこのプロセスが面倒なので、コミットが来たら自動でプッシュしてくれるワークフローをGitHub Actionsを使って構築しました。

```yaml
name: 🚜 Auto update subtree

on:
  push:
    branches:
      - main
    paths:
      - <library_folder>/**
  workflow_dispatch: # 手動で行う場合も考慮

jobs:
  run:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - name: Sync branch using subtree
        run: |
          git subtree push --prefix materialchart origin <subtree_branch>
```

## カスタムビューの作成

```kotlin
class Chart @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        dataset ?: return

        // TODO: グラフの作成

        // TODO: 軸、ラベルの作成

        // TODO: ガイドラインの作成
    }
}
```

カスタムビュークラスは `View` を継承しています。`View` を継承した後、`onDraw` メソッドをオーバーライドしてグラフを描画するロジックを実装すればよい。`onDraw` メソッドは `View` が画面に描画されるたびに呼び出されるメソッドです。`onDraw` の中に入る Canvas オブジェクトは `View` が自分自身?に描画できるオブジェクトなので、グラフを描画するロジックはそのオブジェクトを利用すればよい。



```xml
<your.library.package.name.Chart
    android:id="@+id/example_chart_1"
    android:layout_width="0dp"
    android:layout_height="400dp"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent" />
```

カスタムビューを作成したら、Androidでは上記のようにレイアウトファイルに追加することができます。

次の記事ではグラフの軸をどのように描いたか説明します。