Canvas in Android — ist eine Klasse aus dem Paket android.graphics, die eine API für 2D-Rendering auf View bereitstellt. Canvas funktioniert wie eine Leinwand, auf der der Entwickler mithilfe verschiedener draw*-Methoden geometrische Formen, Text, Bilder und Pfade zeichnet. Laut der Android Developers Documentation (2025) ist Canvas das primäre Werkzeug für benutzerdefiniertes Rendering in Android und wird zusammen mit der Paint-Klasse verwendet, um Stile und Farben zu definieren. Jeder onDraw()-Aufruf erhält eine fertige Canvas-Instanz, die mit dem Bitmap-Puffer der View verbunden ist.
Wichtige Punkte
Canvas — ist die zentrale 2D-Grafikklasse in Android, die Methoden zum Zeichnen auf einer Ebene bereitstellt. Jeder Canvas ist an eine Bitmap gebunden — einen Speicherbereich, in dem die Ergebnisse der Zeichenoperationen aufgezeichnet werden. Wenn eine View neu gezeichnet wird, übergibt das System an onDraw() einen Canvas, der bereits mit dem Puffer dieser View verbunden ist.
Canvas arbeitet in einem Koordinatensystem, in dem (0,0) die obere linke Ecke ist, die X-Achse nach rechts und die Y-Achse nach unten verläuft. Alle Zeichenoperationen verwenden den aktuellen Zustand von Canvas, einschließlich der Transformationsmatrix, des Clip-Bereichs und des Paint-Stils. Laut der Android-Dokumentation (2025) umfasst die Canvas API über 30 draw*-Methoden für verschiedene Arten von Grafikprimitiven.
Seit Android 3.0 (API 11) unterstützt Canvas Hardwarebeschleunigung über DisplayList. Im beschleunigten Modus werden Canvas-Operationen in einer DisplayList aufgezeichnet und dann auf der GPU ausgeführt. Dies bringt eine deutliche Leistungssteigerung, erzwingt jedoch Einschränkungen: Einige Canvas-Operationen (clipPath mit Nicht-Path-Clip) werden im beschleunigten Modus nicht unterstützt.
Canvas bietet eine Reihe von Methoden zum Zeichnen grundlegender Primitive. Jede Methode akzeptiert geometrische Parameter und ein Paint-Objekt, das den visuellen Stil definiert.
| Methode | Zweck | Beispiel |
|---|---|---|
| drawRect() | Zeichnet ein Rechteck | canvas.drawRect(10f, 10f, 100f, 100f, paint) |
| drawCircle() | Zeichnet einen Kreis | canvas.drawCircle(50f, 50f, 30f, paint) |
| drawLine() | Zeichnet eine Linie | canvas.drawLine(0f, 0f, 100f, 100f, paint) |
| drawText() | Zeichnet Text | canvas.drawText(“Hello”, 10f, 20f, paint) |
| drawBitmap() | Zeichnet ein Bild | canvas.drawBitmap(bitmap, 0f, 0f, paint) |
| drawPath() | Zeichnet einen beliebigen Pfad | canvas.drawPath(path, paint) |
| drawOval() | Zeichnet ein Oval | canvas.drawOval(rectF, paint) |
| drawArc() | Zeichnet einen Bogen oder Sektor | canvas.drawArc(rectF, 0f, 90f, true, paint) |
Jede Methode kann in beliebiger Reihenfolge aufgerufen werden — Canvas zeichnet in der Reihenfolge der Aufrufe und überlagert neue Elemente über vorherige. Für komplexe Kompositionen wird die Mehrschicht-Rendering-Technik mit Speichern und Wiederherstellen des Canvas-Zustands verwendet.
Paint — ist die Klasse, die definiert, wie Canvas zeichnen wird. Ohne Paint sind Canvas-Methoden nutzlos — sie setzt Farbe, Linienstärke, Füllstil, Textgröße, Effekte (Schatten, Verlauf, Unschärfe) und den Mischmodus.
Paint hat wichtige Eigenschaften: color (Farbe), style (FILL, STROKE oder FILL_AND_STROKE), strokeWidth (Linienstärke), textSize (Textgröße), isAntiAlias (Antialiasing), shader (Verlauf oder Bild). Das Erstellen eines Paint-Objekts ist eine teure Operation, daher sollte es einmal erstellt und wiederverwendet werden.
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)
}
In diesem Beispiel werden zwei Paint-Objekte verwendet: eines zum Füllen des Kreises mit lila Farbe, das zweite für den weißen Strich. Die Trennung von Füllung und Strich ist eine Standardpraxis, die eine flexible Steuerung des Erscheinungsbilds ohne Neuerstellung von Objekten ermöglicht.
Für Verlauffüllungen akzeptiert Paint einen Shader — die Unterklassen LinearGradient, RadialGradient, SweepGradient. Shader wird über die Methode setShader() an Paint gebunden und definiert, wie sich die Farbe über den Zeichenbereich ändert.
Canvas unterstützt Matrix-Transformationen des Koordinatensystems: translate() (Verschiebung), rotate() (Drehung), scale() (Skalierung) und skew() (Scherung). Transformationen akkumulieren: Jeder nachfolgende Aufruf wird über vorherige angewendet.
Die Verwaltung des Transformationsstapels erfolgt über save() und restore(). save() speichert den aktuellen Canvas-Zustand (Matrix, Clip) auf dem Stapel, restore() stellt den letzten gespeicherten Zustand wieder her. Zwischen save() und restore() können verschachtelte Transformationen angewendet werden.
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
}
Verwenden Sie immer save() vor dem Start von Transformationen und restore() nach deren Abschluss. Unausgeglichene save/restore-Paare führen zur Akkumulation der Matrix und zu falschem Rendering nachfolgender Elemente.
Für komplexe Grafiken wird Canvas mit Bitmap und Path kombiniert. Bitmap wird zum Laden von Bildern und zum Zwischenspeichern gerenderter Frames verwendet. Path wird zum Erstellen beliebiger Bezier-Kurven, Polygone und komplexer Konturen verwendet.
Die Offscreen-Rendering-Technik: Eine Bitmap wird erstellt, eine komplexe Szene wird über einen separaten Canvas darauf gezeichnet, und dann wird die fertige Bitmap über drawBitmap() in onDraw() ausgegeben. Dies ermöglicht die Aktualisierung komplexer Grafiken ohne vollständiges Neuzeichnen bei jedem Frame.
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 wird zum Erstellen komplexer Konturen verwendet: Bezier-Kurven (quadTo, cubicTo), Bögen (arcTo), Linien (lineTo). Path kann für gefüllte Formen geschlossen (close()) oder für Linien offen sein. Die Hauptmethode drawPath() kombiniert Path und Paint für das endgültige Rendering.
Die Leistung des Canvas-Renderings wirkt sich direkt auf die FPS der Anwendung aus. Hauptprobleme: übermäßige Speicherzuweisungen in onDraw(), häufige invalidate()-Aufrufe und suboptimale Nutzung der Hardwarebeschleunigung.
Paint, Path, Rect, RectF und andere Objekte sollten einmal im Initialisierer oder in der setup()-Methode erstellt werden. Jede Speicherzuweisung in onDraw() erzeugt Druck auf den Garbage Collector, was zu Mikroverzögerungen führt. Die Ausnahme sind static oder companion object für unveränderliche Objekte.
Wenn der große Teil der View außerhalb des sichtbaren Bereichs verborgen ist (z.B. in ScrollView), verwenden Sie canvas.clipRect(), um das Rendering zu begrenzen. Die Hardwarebeschleunigung optimiert in diesem Fall zusätzlich das Clipping unsichtbarer Pixel.
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()
}
Verwenden Sie View.setLayerType() mit LAYER_TYPE_HARDWARE für Views mit komplexer Canvas-Grafik, die selten aktualisiert werden. Für häufig aktualisierte Animationen verwenden Sie dagegen LAYER_TYPE_NONE, um teure Hardware-Layer-Updates zu vermeiden. Profilen Sie mit GPU Profile Rendering und Android Studio Profiler.
Häufig gestellte Fragen
Ja, Sie können Canvas(Bitmap) erstellen — einen Konstruktor, der eine Bitmap akzeptiert. Dies wird für Offscreen-Rendering verwendet: Sie zeichnen auf eine Bitmap und geben sie dann über drawBitmap() aus. Canvas kann auch von Surface (für SurfaceView) oder über Picture erstellt werden.
Canvas — ist eine API zum Zeichnen auf View im UI-Thread. SurfaceView — ist ein separates Surface, das aus einem Hintergrund-Thread über einen eigenen Canvas (lockCanvas() / unlockCanvasAndPost()) aktualisiert werden kann. SurfaceView wird für Spiele und Videos bevorzugt.
Der häufigste Grund sind unausgeglichene save()- und restore()-Paare. Wenn nach einer Transformation (translate, rotate) restore() nicht aufgerufen wird, bleibt Canvas in einem modifizierten Koordinatensystem, und nachfolgende Elemente werden mit Versatz gezeichnet. Überprüfen Sie Ihre save()/restore()-Paare.
Verwenden Sie Paint.setTextAlign(Paint.Align.CENTER) für horizontale Zentrierung und Paint.getTextBounds() oder Paint.descent() / ascent() für vertikale. Zentrierung: canvas.drawText(text, centerX, centerY - (ascent + descent) / 2, paint).
Canvas selbst animiert nicht — es zeichnet nur den aktuellen Zustand. Animation wird durch aufeinanderfolgende Aufrufe von invalidate() (oder postInvalidateOnAnimation()) erreicht, wobei jedes Mal ein neuer Frame in onDraw() gezeichnet wird. Für flüssige Animationen verwenden Sie ValueAnimator oder Choreographer zur Synchronisation mit Vsync.
Zusammenfassung
Wir entwickeln eine mobile Applikation schlüsselfertig
IT Sectr entwickelt seit 2017 iOS- und Android-Apps für Startups und Unternehmen. Wir beraten Sie und schlagen die beste Lösung vor.
Lesen Sie auch