
Android Pathが大体わかる! – Android Path, Kotlin
Jetpack Composeで線の描画などをする際に使用するAndriod Pathクラスについて大体わかるようにまとめました。
はじめに
この記事は「Android Pathクラスが大体わかる」を目標にまとめています。
そのためにはoffset(オフセット)の考え方が必要になるかもしれないため、こちらも読んでみてください。
下記は学びやすいと思う順番に筆者が並べました。
参考程度に読んでいただければと思います。
大体わかる順
moveTo
こちらは描画を開始する点を指定する関数です。
画面に対する描画は発生しませんが、引数の値の考え方(offset)がわかりやすいと思います。
relativeMoveTo
こちらはmoveToと基本的には同じですが、offsetが異なります。
しかしこの違いがわかればここから先も大体わかると思います。
lineTo
こちらは開始点から引数で指定した点まで線を引く関数です。
特に複雑なものではなく理解しやすいと思います。
relativeLineTo
こちらはlineToとほぼ同じですが、offsetの考え方が描画開始点という違いがあります。
このように「relativeXXX」という名前のものはoffsetの考え方が違うだけで、基本部分は同じです。
close
こちらはパスの開始点まで線を引く関数です。
dbのclose関数のように呼び出し必須ではありません。
quadraticBezierTo
こちらは2次ベジェ曲線を追加する関数です。
ベジェ曲線が何かも簡単に解説しています。
relativeQuadraticBezierTo
quadraticBezierToのrelative(相対的)バージョンです。
cubicTo
こちらは3次ベジェ曲線を追加する関数です。
ベジェ曲線が何かも簡単に解説しています。
relativeCubicTo
cubicToのrelative(相対的)バージョンです。
addOval
こちらは円を追加する関数です。
半円や部分的に欠けている円などを追加したい場合は下のaddArcかaddArcRadの使用をお勧めします。
addArc
こちらは半円などの円の一部分を追加する関数です。
引数に対する値次第では完全な円も追加できます。
下のaddArcRadとの違いは、引数にしている値の単位が「度」か「ラジアン」である点です。
こちらは「度」で指定します。
addArcRad
addArcと同様に円の部分描画ができます。
こちらの指定単位はラジアンです。
おわりに
ベジェ曲線が少し難しいですが、その他についてはそこまで難しくないかもしれません。
relative系は使用頻度が高い気がするので押さえておきたいですね!
こちらで紹介している関数で大体カバーしていますが全てではないため、他の関数に関しては公式を参照してください。
アイキャッチ画像はAndroidPathクラスの関数で描いたドラえもんっぽい何かです。
コードを置いときます笑
Box( | |
modifier = modifier | |
.fillMaxSize() | |
.drawWithCache { | |
val path = Path() | |
val screenWidth = size.width | |
// eyes | |
// left eye pupil | |
val pupilRadius = 50f | |
val pupilOffsetY = 120f | |
val pupilOfLeftEye = Rect( | |
center = Offset( | |
x = screenWidth / 2 - pupilRadius - 10f, | |
y = pupilOffsetY | |
), | |
radius = pupilRadius | |
) | |
path.addOval( | |
oval = pupilOfLeftEye | |
) | |
// right eye pupil | |
val pupilOfRightEye = Rect( | |
center = Offset( | |
x = screenWidth / 2 + pupilRadius + 10f, | |
y = pupilOffsetY | |
), | |
radius = pupilRadius | |
) | |
path.addOval( | |
oval = pupilOfRightEye | |
) | |
// left eyeball | |
val eyeballRadius = 80f | |
val eyeballOffsetY = 100f | |
val leftEyeball = Rect( | |
center = Offset( | |
x = screenWidth / 2 - eyeballRadius, | |
y = eyeballOffsetY | |
), | |
radius = eyeballRadius | |
) | |
path.addOval( | |
oval = leftEyeball | |
) | |
// right eyeball | |
val rightEyeball = Rect( | |
center = Offset( | |
x = screenWidth / 2 + eyeballRadius, | |
y = eyeballOffsetY | |
), | |
radius = eyeballRadius | |
) | |
path.addOval( | |
oval = rightEyeball | |
) | |
// nose | |
val noseRadius = 40f | |
val noseOffsetY = 190f | |
val nose = Rect( | |
center = Offset( | |
x = screenWidth / 2, | |
y = noseOffsetY | |
), | |
radius = noseRadius | |
) | |
path.addOval( | |
oval = nose | |
) | |
// nose to mouth line | |
path.moveTo( | |
x = screenWidth / 2, | |
y = noseOffsetY + noseRadius | |
) | |
val noseToMouthLineOffsetX = 0f | |
val noseToMouthLineOffsetY = 160f | |
path.relativeLineTo( | |
dx = noseToMouthLineOffsetX, | |
dy = noseToMouthLineOffsetY | |
) | |
// mouth | |
path.relativeMoveTo( | |
dx = noseToMouthLineOffsetX - 200f, | |
dy = -noseToMouthLineOffsetY | |
) | |
path.relativeQuadraticBezierTo( | |
dx1 = 200f, | |
dy1 = 310f, | |
dx2 = 400f, | |
dy2 = 0f | |
) | |
// whiskers | |
// left whiskers | |
path.moveTo( | |
x = screenWidth / 2 - eyeballRadius, | |
y = 230f | |
) | |
path.relativeLineTo( | |
dx = -200f, | |
dy = -100f | |
) | |
path.moveTo( | |
x = screenWidth / 2 - eyeballRadius, | |
y = 250f | |
) | |
path.relativeLineTo( | |
dx = -200f, | |
dy = 0f | |
) | |
path.moveTo( | |
x = screenWidth / 2 - eyeballRadius, | |
y = 270f | |
) | |
path.relativeLineTo( | |
dx = -200f, | |
dy = 100f | |
) | |
// right whiskers | |
path.moveTo( | |
x = screenWidth / 2 + eyeballRadius, | |
y = 230f | |
) | |
path.relativeLineTo( | |
dx = 200f, | |
dy = -100f | |
) | |
path.moveTo( | |
x = screenWidth / 2 + eyeballRadius, | |
y = 250f | |
) | |
path.relativeLineTo( | |
dx = 200f, | |
dy = 0f | |
) | |
path.moveTo( | |
x = screenWidth / 2 + eyeballRadius, | |
y = 270f | |
) | |
path.relativeLineTo( | |
dx = 200f, | |
dy = 100f | |
) | |
// face line | |
path.addArc( | |
oval = Rect( | |
center = Offset( | |
x = screenWidth / 2, | |
y = 300f | |
), | |
radius = 250f | |
), | |
startAngleDegrees = -50f, | |
sweepAngleDegrees = 280f | |
) | |
path.addOval( | |
oval = Rect( | |
center = Offset( | |
x = screenWidth / 2, | |
y = 300f | |
), | |
radius = 400f | |
) | |
) | |
path.translate( | |
offset = Offset( | |
x = 0f, | |
y = size.height / 2 - 500f | |
) | |
) | |
onDrawWithContent { | |
drawContent() | |
drawPath( | |
path = path, | |
color = Color.Black, | |
style = Stroke(width = 10f) | |
) | |
} | |
} | |
) |
質問等がありましたら、お気軽にコメントまでお願いします。
コメント