この記事はレコチョク Advent Calendar 2022の14日目の記事となります。
はじめに
はじめまして、秦基博さんと日向坂46さんに日々癒しと元気をいただいている休井です。
株式会社レコチョクでAndroidの開発をしています。
最近はJetpack Composeを使用してUI作成をすることが多いのですが、何となく難しそうで触れてこなかったCanvas APIについて、実際に使ってみながらご紹介したいと思います。
Canvas APIについて
Androidでは、Jetpack ComposeというUIツールキットが提供されており、宣言的にUIを作成することができます。
CanvasはそのJetpack Composeに含まれるAPIで、画面内の位置(座標)を用いてレイアウトの設定を行うことができるので、かなり自由度高くカスタムレイアウトを作成することができます。
基本的な使い方
- 線を引く
drawLine()を使用します。
startとendで線の開始と終了を指定して線を引くことができます。@Composable fun DrawLine() { Canvas( modifier = Modifier .fillMaxSize() ) { val width = size.width val height = size.height drawLine( color = Color.Black, start = Offset.Zero, end = Offset(width, height), strokeWidth = 5F ) } }
四角形を描く
drawRect()を使用します。
topLeftで描画位置を決め、sizeで大きさを指定して四角形を描くことができます。@Composable fun DrawRect() { Canvas( modifier = Modifier .fillMaxSize() ) { val width = size.width val height = size.height drawRect( color = Color.Black, topLeft = Offset(width / 3, height / 3), size = Size(width / 3, height / 3) ) } } 角丸の四角形にしたい場合は、
drawRoundRect()を使いcornerRadiusに丸の半径を設定します。@Composable fun DrawRoundRect() { Canvas( modifier = Modifier .fillMaxSize() ) { val width = size.width val height = size.height drawRoundRect( color = Color.Black, topLeft = Offset(width / 3, height / 3), size = Size(width / 3, height / 3), cornerRadius = CornerRadius(x = 100F, y = 100F) ) } }
円を描く
drawCircle()を使用します。centerで描画位置を決め、radiusで半径を設定して円を描くことができます。@Composable fun DrawCircle() { Canvas( modifier = Modifier .fillMaxSize() ) { val width = size.width val height = size.height drawCircle( color = Color.Black, radius = 400F, center = Offset(width / 2, height / 2) ) } } また、楕円を描きたい場合は、
drawOval()を使用します。
topLeftで描画位置を決め、sizeで大きさを設定して楕円を描くことができます。@Composable fun DrawOval() { Canvas( modifier = Modifier .fillMaxSize() ) { val width = size.width val height = size.height drawOval( color = Color.Black, topLeft = Offset(width / 4, height / 3), size = Size(width = width / 2, height = height / 5) ) } }
弧を描く
drawArc()を使用します。
startAngleで開始位置(0Fが3時の方向)を決め、sweepAngleで弧の角度を設定して弧を描くことができます。
また、useCenterをtrueにすると、扇型に描画することができます。@Composable fun DrawArc() { Canvas( modifier = Modifier .fillMaxSize() ) { val width = size.width drawArc( color = Color.Black, startAngle = 0F, sweepAngle = 90F, useCenter = true, size = Size(width = width / 2, height = width / 2) ) drawArc( color = Color.Black, startAngle = 0F, sweepAngle = 90F, useCenter = false, size = Size(width = width / 2, height = width / 2), style = Stroke(width = 5F), topLeft = Offset(0F, width / 2) ) } }
点を描く
drawPoints()を使用します。
pointsで描きたい点の位置をリストで指定して点を描くことができます。
また、pointModeで描き方、capで点の形を設定できます。@Composable fun DrawPoints() { Canvas( modifier = Modifier .fillMaxSize() ) { val height = size.height drawPoints( points = listOf( Offset(300F, height / 2), Offset(500F, height / 2), Offset(700F, height / 2), Offset(900F, height / 2), Offset(1100F, height / 2), ), pointMode = PointMode.Points, color = Color.Black, strokeWidth = 50F, cap = StrokeCap.Square ) } }
パスを決めて描く
drawPath()を使用します。
pathを作成しdrawPath()に渡すことで、作成したpathに色を付けることができます。@Composable fun DrawPath() { Canvas( modifier = Modifier .fillMaxSize() ) { val width = size.width val height = size.height val path = Path() path.moveTo(width / 2, height / 3) // pathの開始位置 path.lineTo(width / 2, height / 2) // 開始位置→(width / 2, height / 2) path.lineTo(width, height / 3) // (width / 2, height / 2)→(width, height / 3) drawPath( path = path, color = Color.Black ) } }
使ってみた
Canvasでできることと使い方の基本について確認できたので、単純なアナログ時計のレイアウトを描いてみました。
@Composable
fun Clock() {
Canvas (
modifier = Modifier
.size(360.dp)
) {
drawCircle(
color = Color.Black,
radius = 180.dp.toPx(),
center = center,
style = Stroke(width = 3.dp.toPx())
)
drawPoints(
points = listOf(
center,
Offset(180.dp.toPx(), 12.dp.toPx()),
Offset(264.dp.toPx(), 35.dp.toPx()),
Offset(325.dp.toPx(), 97.dp.toPx()),
Offset(348.dp.toPx(), 180.dp.toPx()),
Offset(325.dp.toPx(), 263.dp.toPx()),
Offset(264.dp.toPx(), 325.dp.toPx()),
Offset(180.dp.toPx(), 348.dp.toPx()),
Offset(96.dp.toPx(), 325.dp.toPx()),
Offset(35.dp.toPx(), 263.dp.toPx()),
Offset(12.dp.toPx(), 180.dp.toPx()),
Offset(35.dp.toPx(), 97.dp.toPx()),
Offset(96.dp.toPx(), 35.dp.toPx()),
),
pointMode = PointMode.Points,
color = Color.Black,
cap = StrokeCap.Square,
strokeWidth = 5.dp.toPx()
)
drawLine(
color = Color.Black,
start = center,
end = Offset(center.x, 40.dp.toPx()),
strokeWidth = 5.dp.toPx()
)
drawLine(
color = Color.Black,
start = center,
end = Offset(center.x + 100.dp.toPx(), center.y),
strokeWidth = 5.dp.toPx()
)
}
}

centerでCanvasの中心のOffsetを取得したり、dp.toPx()でDpからピクセルへの変換をしたりもできるので、機種によってレイアウトが崩れるという心配も少なそうですね。
おわりに
今回はJetpack ComposeのCanvas APIの使い方についてご紹介しました。
私自身今回でほぼ初めて触れましたが、シンプルなレイアウトであればさほど難しくなく作れそうです。
アニメーションをつけたり、テキストを入れたりすることもできるようなので、今後勉強して使いこなせるようになりたいと思います。
最後まで読んでいただきありがとうございました。
明日のレコチョク Advent Calendar 2022は15日目 シンプルな掲示板を素のPHPとCakePHPで作って比較する となります。お楽しみに!
chiaki.kyui