AndroidのCanvas — android.graphicsパッケージのクラスで、View上での2DレンダリングのためのAPIを提供します。Canvasはキャンバスとして機能し、開発者はさまざまなdraw*メソッドを使用して幾何学的図形、テキスト、画像、パスを描画します。Androidデベロッパーズドキュメンテーション(2025)によると、CanvasはAndroidでのカスタムレンダリングの主要ツールであり、スタイルと色を定義するためにPaintクラスと一緒に使用されます。各onDraw()呼び出しは、ビューのビットマップバッファに関連付けられた準備済みのCanvasインスタンスを受け取ります。
重要なポイント
Canvas — Androidの中心的な2Dグラフィックスクラスであり、平面上に描画するためのメソッドを提供します。各CanvasはBitmapに関連付けられています — 描画操作の結果が記録されるメモリ領域です。Viewが再描画されると、システムはonDraw()にそのViewのバッファにすでに関連付けられたCanvasを渡します。
Canvasは、(0,0)が左上隅、X軸が右、Y軸が下の座標系で動作します。すべての描画操作は、変換行列、クリッピング領域、Paintスタイルを含むCanvasの現在の状態を使用します。Androidドキュメンテーション(2025)によると、Canvas APIにはさまざまな種類のグラフィックプリミティブのための30以上のdraw*メソッドが含まれています。
Android 3.0(API 11)以降、CanvasはDisplayListを介したハードウェアアクセラレーションをサポートしています。アクセラレーションモードでは、Canvas操作はDisplayListに記録され、GPUで実行されます。これにより大幅なパフォーマンス向上が得られますが、制限もあります。一部のCanvas操作(非PathクリップでのclipPath)はアクセラレーションモードでサポートされていません。
Canvasは基本的なプリミティブを描画するための一連のメソッドを提供します。各メソッドは幾何学的パラメータと、視覚スタイルを定義するPaintオブジェクトを受け入れます。
| メソッド | 目的 | 例 |
|---|---|---|
| drawRect() | 長方形を描画 | canvas.drawRect(10f, 10f, 100f, 100f, paint) |
| drawCircle() | 円を描画 | canvas.drawCircle(50f, 50f, 30f, paint) |
| drawLine() | 線を描画 | canvas.drawLine(0f, 0f, 100f, 100f, paint) |
| drawText() | テキストを描画 | canvas.drawText(“Hello”, 10f, 20f, paint) |
| drawBitmap() | 画像を描画 | canvas.drawBitmap(bitmap, 0f, 0f, paint) |
| drawPath() | 任意のパスを描画 | canvas.drawPath(path, paint) |
| drawOval() | 楕円を描画 | canvas.drawOval(rectF, paint) |
| drawArc() | 円弧または扇形を描画 | canvas.drawArc(rectF, 0f, 90f, true, paint) |
各メソッドは任意の順序で呼び出すことができます — Canvasは呼び出し順に描画し、新しい要素を前の要素の上に重ねます。複雑な構成では、Canvas状態の保存と復元を使用したマルチレイヤーレンダリング手法が使用されます。
Paint — Canvasがどのように描画するかを定義するクラスです。PaintなしではCanvasメソッドは無意味です — 色、線の太さ、塗りつぶしスタイル、テキストサイズ、効果(影、グラデーション、ぼかし)およびブレンドモードを設定します。
Paintには主要なプロパティがあります: color(色)、style(FILL、STROKE、またはFILL_AND_STROKE)、strokeWidth(線の太さ)、textSize(テキストサイズ)、isAntiAlias(アンチエイリアシング)、shader(グラデーションまたは画像)。Paintオブジェクトの作成はコストのかかる操作であるため、一度作成して再利用する必要があります。
private val fillPaint = Paint().apply {
color = Color.parseColor("#FF6200EE")
style = Paint.Style.FILL
isAntiAlias = true
}
private val strokePaint = Paint().apply {
color = Color.WHITE
style = Paint.Style.STROKE
strokeWidth = 4f
isAntiAlias = true
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
canvas.drawCircle(100f, 100f, 80f, fillPaint)
canvas.drawCircle(100f, 100f, 80f, strokePaint)
}
この例では、2つのPaintオブジェクトが使用されています。1つは円を紫色で塗りつぶすため、もう1つは白いストロークのためです。塗りつぶしとストロークを分離することは、オブジェクトを再作成せずに外観を柔軟に制御できる標準的なプラクティスです。
グラデーションの塗りつぶしのために、PaintはShaderを受け入れます — サブクラスLinearGradient、RadialGradient、SweepGradient。ShaderはsetShader()メソッドを介してPaintにバインドされ、描画領域全体で色がどのように変化するかを定義します。
Canvasは座標系の行列変換をサポートしています: translate()(移動)、rotate()(回転)、scale()(拡大縮小)、skew()(傾斜)。変換は累積されます。後続の各呼び出しは前の呼び出しの上に適用されます。
変換スタックの管理はsave()とrestore()を介して行われます。save()は現在のCanvas状態(行列、クリップ)をスタックに保存し、restore()は最後に保存された状態を復元します。save()とrestore()の間では、ネストされた変換を適用できます。
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
canvas.save() // Save initial state
canvas.translate(100f, 100f) // Translate origin
// First square
canvas.drawRect(0f, 0f, 50f, 50f, fillPaint)
canvas.save() // Save after translate
canvas.rotate(45f, 25f, 25f) // Rotate around square center
// Rotated square
canvas.drawRect(0f, 0f, 50f, 50f, strokePaint)
canvas.restore() // Restore after rotate
canvas.restore() // Restore initial state
}
変換を開始する前には必ずsave()を、変換を完了した後にはrestore()を使用してください。不均衡なsave/restoreは行列の累積と後続要素の不正確なレンダリングを引き起こします。
複雑なグラフィックのために、CanvasはBitmapおよびPathと組み合わせて使用されます。Bitmapは画像の読み込みとレンダリングされたフレームのキャッシュに使用されます。Pathは任意のベジェ曲線、多角形、複雑な輪郭を構築するために使用されます。
オフスクリーンレンダリング手法: Bitmapを作成し、別のCanvasを介して複雑なシーンをその上に描画し、完成したBitmapをdrawBitmap()を介してonDraw()で出力します。これにより、毎フレームの完全な再描画なしで複雑なグラフィックを更新できます。
private var offscreenBitmap: Bitmap? = null
private var offscreenCanvas: Canvas? = null
fun buildOffscreenScene(width: Int, height: Int) {
offscreenBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
offscreenCanvas = Canvas(offscreenBitmap)
// Render scene once
offscreenCanvas!!.drawColor(Color.WHITE)
drawGrid(offscreenCanvas!!)
drawComplexShape(offscreenCanvas!!)
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
offscreenBitmap?.let { canvas.drawBitmap(it, 0f, 0f, null) }
}
Pathは複雑な輪郭の構築に使用されます: ベジェ曲線(quadTo、cubicTo)、円弧(arcTo)、線(lineTo)。Pathは塗りつぶし形状の場合は閉じる(close())ことも、線の場合は開いたままにすることもできます。主要なdrawPath()メソッドはPathとPaintを組み合わせて最終的なレンダリングを行います。
CanvasレンダリングのパフォーマンスはアプリケーションのFPSに直接影響します。主な問題: onDraw()での過剰なメモリ割り当て、頻繁なinvalidate()呼び出し、ハードウェアアクセラレーションの最適でない使用。
Paint、Path、Rect、RectFなどのオブジェクトは、初期化子またはsetup()メソッドで一度だけ作成する必要があります。onDraw()での各メモリ割り当てはガベージコレクタに負荷をかけ、マイクロラグを引き起こします。例外は、不変オブジェクトのためのstaticまたはcompanion objectです。
Viewの大部分が可視領域の外に隠れている場合(たとえばScrollView内)、レンダリングを制限するためにcanvas.clipRect()を使用します。この場合、ハードウェアアクセラレーションは非表示ピクセルのクリッピングをさらに最適化します。
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
// Clip to visible area
canvas.save()
canvas.clipRect(scrollX, scrollY, scrollX + width, scrollY + height)
// Render large scene
renderScene(canvas)
canvas.restore()
}
更新が稀な複雑なCanvasグラフィックを持つViewには、LAYER_TYPE_HARDWAREを指定したView.setLayerType()を使用します。頻繁に更新されるアニメーションの場合は、逆にLAYER_TYPE_NONEを使用して高コストなハードウェアレイヤーの更新を避けます。GPU Profile RenderingとAndroid Studio Profilerを使用してプロファイリングします。
よくある質問
はい、Canvas(Bitmap)を作成できます — Bitmapを受け取るコンストラクタです。これはオフスクリーンレンダリングに使用されます。Bitmapに描画し、drawBitmap()を介して出力します。CanvasはSurface(SurfaceView用)またはPictureからも作成できます。
Canvas — UIスレッドでViewに描画するためのAPIです。SurfaceView — 独自のCanvas(lockCanvas() / unlockCanvasAndPost())を介してバックグラウンドスレッドから更新できる別個のSurfaceです。SurfaceViewはゲームやビデオに適しています。
最も一般的な原因は、save()とrestore()の不均衡です。変換(translate、rotate)後にrestore()が呼び出されないと、Canvasは変更された座標系のままになり、後続の要素がオフセットして描画されます。save()/restore()のペアを確認してください。
水平方向の中央揃えにはPaint.setTextAlign(Paint.Align.CENTER)、垂直方向にはPaint.getTextBounds()またはPaint.descent() / ascent()を使用します。中央揃え: canvas.drawText(テキスト, centerX, centerY - (ascent + descent) / 2, paint)。
Canvas自体はアニメーションしません — 現在の状態を描画するだけです。アニメーションはinvalidate()(またはpostInvalidateOnAnimation())の連続呼び出しによって実現され、毎回onDraw()で新しいフレームを描画します。スムーズなアニメーションのためには、Vsyncと同期するためにValueAnimatorまたはChoreographerを使用します。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。