onDraw() ist eine Schlüsselmethode der View-Klasse im Android SDK, die für das Zeichnen des Inhalts einer Ansicht verantwortlich ist. Das System ruft onDraw bei jeder Aktualisierung des Erscheinungsbilds der View auf — beim ersten Rendern, nach invalidate() oder bei Größenänderungen. Laut der Android Developers Documentation (2026) ist das Überschreiben von onDraw mit der Canvas API die primäre Möglichkeit, benutzerdefinierte Grafiken in Android-Anwendungen zu erstellen.
Das Wichtigste
onDraw(Canvas canvas) ist eine protected-Methode der Klasse android.view.View, die vom Android-System aufgerufen wird, wenn eine View gezeichnet werden muss. Der Entwickler überschreibt diese Methode, um benutzerdefinierte Zeichnungen zu implementieren: Formen, Text, Bilder, Verläufe, Animationen. Der Canvas-Parameter ist eine grafische Oberfläche, auf der alle Zeichenoperationen ausgeführt werden.
Die Methodensignatur ist einfach: override fun onDraw(canvas: Canvas). Innerhalb dieser Methode sollten keine neuen Objekte erstellt (um GC-Überlastung zu vermeiden), keine langen Operationen ausgeführt oder Netzwerkzugriffe getätigt werden. View enthält die onDraw-Methode in ihrem Lebenszyklus zwischen onLayout (Positionsberechnung) und dispatchDraw (Zeichnen von Kindelementen).
Beim Erben von View ist das Überschreiben von onDraw für die Anzeige jeglichen Inhalts erforderlich — andernfalls bleibt die View leer. Beim Erben von vorhandenen Unterklassen (TextView, ImageView, Button) ist das Überschreiben von onDraw optional und wird verwendet, um zusätzliche Grafiken über den Standardinhalt zu legen.
Android führt das Zeichnen der View in einer streng definierten Reihenfolge aus, die vom Hauptthread (UI-Thread) aufgerufen wird. Der vollständige Zyklus besteht aus drei Phasen: measure (Maßberechnung), layout (Positionsberechnung) und draw (Rendering). onDraw ist Teil der draw-Phase und wird aufgerufen, nachdem die Maße und Position der View bereits bestimmt wurden.
draw(Canvas canvas) ist eine öffentliche Methode von View, die onDraw und dispatchDraw aufruft. Die draw-Methode ist nicht zum Überschreiben gedacht — stattdessen wird onDraw überschrieben. Nach der Ausführung von onDraw für die aktuelle View wird dispatchDraw aufgerufen, das rekursiv draw für alle untergeordneten Views aufruft (wenn die aktuelle View ein ViewGroup-Container ist).
onDraw wird unter folgenden Bedingungen aufgerufen: 1) die View wird zum ersten Mal sichtbar (nach setVisibility(VISIBLE)); 2) die App ruft invalidate() für diese View oder ihr Eltern-Element auf; 3) die Größe der View ändert sich (neues Layout); 4) eine mit der View verbundene Animation ändert sich (via ValueAnimator oder ObjectAnimator); 5) der Zustand der View ändert sich (pressed, focused, enabled).
Seit Android 3.0 (API 11) werden die meisten Canvas-Operationen hardwarebeschleunigt über die GPU ausgeführt. Der Entwickler kann über canvas.isHardwareAccelerated() überprüfen, ob die Beschleunigung aktiviert ist. Bei Hardwarebeschleunigung werden einige Canvas-Operationen nicht unterstützt (z.B. clipPath mit einem nicht-rechteckigen Bereich) und können ignoriert werden oder Fehler verursachen.
Canvas bietet eine umfangreiche Sammlung von Zeichenmethoden. Alle Methoden arbeiten im aktuellen Koordinatensystem, das durch Transformationen (translate, rotate, scale, skew) geändert werden kann. Jede Methode akzeptiert ein Paint-Objekt, das Farbe, Stil, Linienstärke, Effekte und Anti-Aliasing definiert.
| Canvas-Methode | Zweck | Wichtige Paint-Parameter |
|---|---|---|
| drawCircle | Kreis zeichnen | centerX, centerY, radius, paint |
| drawRect | Rechteck zeichnen | left, top, right, bottom, paint |
| drawLine | Linie zeichnen | startX, startY, endX, endY, paint |
| drawPath | Beliebigen Pfad zeichnen | path, paint |
| drawBitmap | Bitmap-Bild zeichnen | bitmap, srcRect, dstRect, paint |
| drawText | Text zeichnen | text, x, y, paint |
| drawArc | Bogen oder Sektor zeichnen | ovalRect, startAngle, sweepAngle, useCenter, paint |
| drawOval | Oval zeichnen | ovalRect, paint |
Paint definiert den Zeichenstil: Farbe (setColor), Linienstärke (setStrokeWidth), Füllstil (setStyle — FILL, STROKE, FILL_AND_STROKE), Anti-Aliasing (setAntiAlias), Effekte (setShadowLayer, setMaskFilter) und Verläufe (setShader). Es wird empfohlen, Paint einmal im View-Konstruktor zu erstellen und zu konfigurieren, nicht innerhalb von onDraw, um die Objekterstellung in der Zeichenschleife zu vermeiden.
Canvas verwendet ein Koordinatensystem, bei dem die X-Achse nach rechts und die Y-Achse nach unten zeigt. Der Ursprung (0, 0) ist die obere linke Ecke der View. Vor dem Aufruf von Zeichenmethoden kann der Entwickler das Koordinatensystem über canvas.translate(dx, dy), canvas.rotate(degrees, px, py), canvas.scale(sx, sy, px, py) ändern. Alle nachfolgenden Zeichenoperationen werden im transformierten System ausgeführt.
onDraw wird im Haupt-UI-Thread ausgeführt, und seine Dauer wirkt sich direkt auf die Bildrate (fps) aus. Um 60 fps zu halten, muss die onDraw-Methode innerhalb von 16 Millisekunden abgeschlossen sein. Jede Verlangsamung führt zu Bildaussetzern (Jank), die visuell als Ruckeln der Animation oder Verzögerung beim Scrollen wahrgenommen werden.
Wichtige Regeln zur Optimierung von onDraw: 1) Keine Objekte innerhalb von onDraw erstellen — alle Paint-, Path- und Rect-Objekte sollten Klassenfelder sein; 2) Keinen Speicher allozieren (new) — dies löst Garbage Collection (GC) und Frame-Drops aus; 3) Komplexe Operationen über Bitmap.createBitmap in einer Bitmap zwischenspeichern und die vorbereitete Bitmap zeichnen; 4) Keine E/A-Operationen oder Ressourcen laden.
clipRect schränkt den Zeichenbereich auf ein bestimmtes Rechteck ein. Es wird verwendet, um zu verhindern, dass Canvas Zeit damit verbringt, Pixel außerhalb des sichtbaren Bereichs zu zeichnen. Besonders effektiv beim Scrollen großer Listen oder beim Zoomen: Setzen Sie clipRect auf den sichtbaren Bereich (dirty rect), und Canvas zeichnet nur das, was sichtbar ist.
canvas.save() speichert den aktuellen Canvas-Zustand (Transformation und Clip) auf einem Stack. canvas.restore() stellt den zuletzt gespeicherten Zustand wieder her. Dies ist für temporäre Transformationen notwendig: Zustand speichern, translate/rotate anwenden, zeichnen, dann wiederherstellen. save und restore sind leichtgewichtig und beeinträchtigen die Leistung bei angemessener Nutzung (weniger als 10–15 Verschachtelungsebenen) nicht.
invalidate() ist eine View-Methode, die dem System mitteilt, dass der Inhalt der View veraltet ist und neu gezeichnet werden muss. Der Aufruf von invalidate initiiert einen neuen draw-Zyklus, in dem onDraw aufgerufen wird. invalidate darf nur vom UI-Thread (Hauptthread) aus aufgerufen werden. Für den Aufruf aus einem Hintergrundthread wird postInvalidate verwendet.
postInvalidate() ist eine threadsichere Version von invalidate, die eine Neuzeichnungsanforderung über Handler an die Warteschlange des Hauptthreads sendet. Sie wird in Hintergrundaufgaben verwendet, wenn die View nach Abschluss des Datenladens oder von Berechnungen aktualisiert werden muss. Sie kann mit runOnUiThread zur UI-Synchronisation kombiniert werden.
Für teilweises Neuzeichnen verwenden Sie invalidate(Rect dirty) oder invalidate(int l, int t, int r, int b). Dies teilt dem System mit, dass sich nur der angegebene Bereich geändert hat, nicht die gesamte View. Android optimiert das Zeichnen, indem es nur das dirty rect neu zeichnet, was die Leistung bei Animationen mit teilweisen Aktualisierungen erheblich verbessert.
Schauen wir uns praktische Beispiele zum Überschreiben von onDraw zum Erstellen benutzerdefinierter Views in Android mit Kotlin an.
Dieses Beispiel erstellt einen kreisförmigen Fortschrittsindikator mit animierter Füllung. onDraw zeichnet zwei Kreise: einen grauen Hintergrundkreis und einen farbigen Füllkreis, dessen Bogen durch den aktuellen Fortschritt bestimmt wird. Der Aufruf von invalidate in setProgress stellt die Neuzeichnung sicher, wenn sich der Wert ändert.
class CircularProgressView
@JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null
) : View(context, attrs) {
private val bgPaint = Paint().apply {
style = Paint.Style.STROKE
strokeWidth = 12f
color = Color.parseColor("#E0E0E0")
isAntiAlias = true
}
private val progressPaint = Paint().apply {
style = Paint.Style.STROKE
strokeWidth = 12f
color = Color.BLUE
isAntiAlias = true
strokeCap = Paint.Cap.ROUND
}
var progress: Float = 0f
set(value) {
field = value.coerceIn(0f, 1f)
invalidate()
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
val size = minOf(width, height) - 24f
val oval = RectF(12f, 12f,
12f + size, 12f + size)
canvas.drawArc(oval, 270f, 360f, false, bgPaint)
canvas.drawArc(oval, 270f,
progress * 360f,
false, progressPaint)
}
}
Dieses Beispiel zeichnet ein Rechteck mit einem Verlauf und Text darüber. Der Verlauf wird über LinearGradient (Shader) erstellt, der Text über drawText. Beide Paint-Objekte werden einmal im Konstruktor erstellt, während onDraw nur die Rechteckgröße basierend auf der aktuellen View-Breite aktualisiert.
class GradientTextView(context: Context)
: View(context) {
private val shaderPaint = Paint().apply {
isAntiAlias = true
}
private val textPaint = Paint().apply {
color = Color.WHITE
textSize = 48f
isAntiAlias = true
textAlign = Paint.Align.CENTER
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
val gradient = LinearGradient(
0f, 0f, width.toFloat(), 0f,
Color.parseColor("#FF6B35"),
Color.parseColor("#FF0040"),
Shader.TileMode.CLAMP
)
shaderPaint.shader = gradient
canvas.drawRect(0f, 0f,
width.toFloat(),
height.toFloat(), shaderPaint)
val xCenter = width / 2f
val yCenter = height / 2f
val yOffset = (textPaint.descent() +
textPaint.ascent()) / 2f
canvas.drawText("Hello from Canvas",
xCenter, yCenter - yOffset,
textPaint)
}
}
Dieses Beispiel erstellt eine animierte Uhr, die die Zeichnung alle 1000 Millisekunden aktualisiert. postDelayed innerhalb von onDraw ruft nach einer Sekunde invalidate auf und erzeugt so eine endlose Neuzeichnungsschleife. Die Uhrzeiger werden über drawLine mit unterschiedlichen Längen und Dicken gezeichnet.
class AnalogClockView(context: Context)
: View(context) {
private val handPaint = Paint().apply {
style = Paint.Style.STROKE
isAntiAlias = true
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
val cx = width / 2f
val cy = height / 2f
val radius = minOf(cx, cy) - 20f
val now = Calendar.getInstance()
val hours = now.get(Calendar.HOUR) % 12
val minutes = now.get(Calendar.MINUTE)
val seconds = now.get(Calendar.SECOND)
val secondAngle = Math.toRadians(
seconds * 6f - 90f)
handPaint.strokeWidth = 2f
handPaint.color = Color.RED
canvas.drawLine(cx, cy,
cx + cos(secondAngle) * radius * 0.8f,
cy + sin(secondAngle) * radius * 0.8f,
handPaint)
postDelayed({ invalidate() }, 1000)
}
}
Häufig gestellte Fragen
Nein, onDraw sollte nicht direkt aufgerufen werden. Verwenden Sie zum Anfordern einer Neuzeichnung invalidate() (vom UI-Thread) oder postInvalidate() (von einem Hintergrundthread). Das System entscheidet, wann onDraw aufgerufen wird, und optimiert das Zeichnen durch Zusammenfassen mehrerer invalidate-Aufrufe in einem Durchgang.
Gründe: Die View ist ausgeblendet (setVisibility(GONE) oder INVISIBLE); die View wurde nicht zur Window-Hierarchie hinzugefügt; die View-Größe ist 0 (Breite oder Höhe = 0); setWillNotDraw(true) wird aufgerufen (standardmäßig true für ViewGroup). Überprüfen Sie beim Debuggen jeden dieser Punkte.
Bei 60 fps kann onDraw bis zu 60 Mal pro Sekunde aufgerufen werden. Bei 120 fps (auf Geräten mit 120 Hz Displays) — bis zu 120 Mal. Jeder Aufruf sollte innerhalb von etwa 8–16 Millisekunden abgeschlossen sein. Während aktiver Animationen über ObjectAnimator oder ValueAnimator wird die Häufigkeit durch die Anzahl der Animationsframes bestimmt.
onDraw ist für das Zeichnen des eigenen Inhalts der View verantwortlich. dispatchDraw ist für das Zeichnen der untergeordneten Views (in ViewGroup) verantwortlich. dispatchDraw wird nach onDraw aufgerufen und durchläuft rekursiv alle untergeordneten Elemente. Für eine normale View (keine ViewGroup) ist dispatchDraw leer.
Ja, Canvas kann für eine Bitmap über Canvas(bitmap) erstellt und in jedem Thread gezeichnet werden. Dies wird verwendet, um komplexe Grafiken im Hintergrund vorzubereiten. Der an onDraw übergebene Canvas gehört jedoch dem System und darf nur im UI-Thread innerhalb der onDraw-Methode verwendet werden.
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