【Android】独自のViewを描く

# Androidで独自のViewを描く
（当記事はJetPack Compose は使ってません）
円と半円を組み合わせて以下を作ります。

<img width="240" alt="陰陽玉.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/700755/5337480f-012f-a3a1-8efa-416b7c21a17d.png">


## 手順
1. Viewを継承したカスタムクラスを作成
2. onDraw(canvas) をオーバーロード
3. canvasを組み合わせ、欲しいViewを描く ←メイン
4. （ Layoutファイルに1を配置 )


### 1 Viewを継承したカスタムクラスを作成

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

※JvmOverloadsは自動でパラメータを設定してくれます。
Class : View を書いて、右クリック→Jvm～～を選択。

### 2 onDraw(canvas) をオーバーロード
```
override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
....
}
```
onDraw内に外観を描いていきます。

### 3 canvasを組み合わせ、欲しいViewを描く

[Canvas](https://developer.android.com/reference/android/graphics/Canvas/) :公式


> The Canvas class holds the "draw" calls. To draw something, you need 4 basic components: A Bitmap to hold the pixels, a Canvas to host the draw calls (writing into the bitmap), a drawing primitive (e.g. Rect, Path, text, Bitmap), and a paint (to describe the colors and styles for the drawing).

描画用のクラス。各関数は基本的に4つのパラメータを渡して描画すると書いてある。

onDrawのパラメータとして渡されるCanvasでViewの外観を描いていきます。

今回使ったのは円と半円を描く関数です。
 
- canvas.drawCircle で円を描く
- canvas.drawArc で半円を描く
- 組み合わせる

の3ステップです。

#### canvas.drawCircle で円を描く

```
public void drawCircle (float cx, 
                float cy, 
                float radius, 
                Paint paint)
```

x,y 座標、半径、Paintオブジェクトを渡します。
※[Paint](https://developer.android.google.cn/reference/android/graphics/Paint.Style) はViewのスタイルを定義するオブジェクトです。

例えば円を描くなら

```
canvas.drawCircle(baseX, baseY, radius, paint.apply { color = redColor })
```
<img width="240" alt="赤円.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/700755/172e064d-bf65-a63e-6a14-e7e0cc986a49.png">

オフセット分縮めて、白い円を描けば枠線が表現できます。


```
 val mainRadius = radius - OFFSET
it.drawCircle(baseX, baseY, mainRadius, paint.apply { color = whiteColor })
```
<img width="240" alt="白丸.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/700755/dfd8c730-1148-b745-39da-571099c265ca.png">

#### canvas.drawArc で半円を描く
楕円を描く関数drawArcで半円を表現します。

```
public void drawArc (RectF oval, 
                float startAngle, 
                float sweepAngle, 
                boolean useCenter, 
                Paint paint)

※RectF(left, top, right, bottom) 矩形の4つの座標
```
userSenter : 描かれた弧を塗りつぶすときに始点と終点を直線で結ぶか、中心で結ぶ範囲を塗りつぶすかを設定します。


```
val rectF = RectF(left, top, right, bottom)
canvas.drawArc(rectF, 90f, 180f, true,  paint.apply { color = redColor })
```
事前に定義しておいたRectFの座標を使って、90→180度に子を描きます。UserSenterはTrueにすることで中心を結ぶ範囲が塗りつぶされます。

<img width="240" alt="半円.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/700755/36c57933-dc2c-11f0-ccaf-0bb07336adef.png">


#### 組み合わせる
あとは、サイズの違う円を置していくだけです。

```
// 上の中円と小円
canvas.drawCircle(baseX ,topMiddleCircleY , middleCircleRadius, paint.apply { color = redColor })
canvas.drawCircle(baseX, topMiddleCircleY, 20.0f, paint.apply { color = whiteColor })

// 下の中円と小円
canvas.drawCircle(baseX, bottomMiddleCircleY, middleCircleRadius, paint.apply { color = whiteColor })
canvas.drawCircle(baseX, bottomMiddleCircleY, 20.0f, paint.apply { color = redColor })
```
<img width="240" alt="陰陽玉.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/700755/5337480f-012f-a3a1-8efa-416b7c21a17d.png">

### 4 Layoutファイルに定義

これは書くだけです。

```
<com.example.a1_creatingcustomviews.Onmyoudama
        android:id="@ 
...
/>
```

以上。

## 注意点

- onDraw内に重い処理は書かない
<br>
画面更新のたびにonDrawが呼ばれます。以下公式。

> For performance reasons and to avoid visual glitches, you should do as little work as possible in onDraw(). In particular, don't place allocations in onDraw(), because allocations may lead to a garbage collection that may cause a visual stutter.


##　終わり
Glipにて、円を切り取る方法は浮かんでますがちょっとはまったのでこっちの方法になりました。
JetPackCompose版もそのうち

参考
・公式ドキュメント Codelab
・https://tech.recruit-mp.co.jp/mobile/remember_canvas1/