dispatchDraw() ist eine Methode der ViewGroup-Klasse, die für das rekursive Zeichnen aller Child-Views in der Android-Hierarchie verantwortlich ist. Laut Android Developers Documentation (2026) wird dispatchDraw automatisch nach onDraw in der draw()-Methode der Haupt-View aufgerufen und durchläuft alle Kindelemente, um deren eigenes draw() aufzurufen. Entwickler überschreiben dispatchDraw in benutzerdefinierten ViewGroups, um Effekte hinzuzufügen, Grafiken über Kinder zu legen oder die Zeichenreihenfolge zu ändern.
Wichtigste Punkte
dispatchDraw(Canvas canvas) ist eine protected-Methode der ViewGroup-Klasse, die das Android-System aufruft, um alle Child-Views des aktuellen Containers zu zeichnen. dispatchDraw ist Teil der Standard-Zeichen-Pipeline: Zunächst wird onDraw ausgeführt (Zeichnen des View-eigenen Inhalts), dann dispatchDraw (rekursives Zeichnen der Kinder), dann onDrawForeground (Zeichnen des Vordergrunds und der Scrollleisten). Die Methode ist nicht für den direkten Aufruf aus Anwendungscode vorgesehen.
Die Standardimplementierung von dispatchDraw in ViewGroup durchläuft alle Child-Views, überprüft ihre Sichtbarkeit und ruft draw(Canvas) für jede auf. Die Durchlaufreihenfolge entspricht den Kinderindizes (von 0 bis childCount - 1). Wenn sich Child-Views überlappen, werden die späteren in der Liste über den früheren gezeichnet. Durch Ändern der Reihenfolge in dispatchDraw kann der Z-Order des Zeichnens verändert werden.
Für eine View (keine ViewGroup) ist die dispatchDraw-Methode leer — eine normale View hat keine Kindelemente, also gibt es nichts zu zeichnen. Die Methode existiert in der Basisklasse View, wird aber nur auf ViewGroup-Instanzen aufgerufen. Ein Entwickler kann über das Standardverhalten von dispatchDraw prüfen, ob eine View Kindelemente hat, aber oft ist die Prüfung instanceof ViewGroup einfacher.
Die draw(Canvas)-Methode der View-Klasse organisiert eine dreistufige Zeichen-Pipeline. Die erste Stufe ist der Aufruf von onDraw(canvas), bei dem die View ihren eigenen Inhalt zeichnet. Die zweite Stufe ist der Aufruf von dispatchDraw(canvas), der das Zeichnen der Child-Views auslöst. Die dritte Stufe ist onDrawForeground(canvas), zuständig für die Vordergrundschicht, Bildlaufleisten und Ripple-Effekte. Diese Reihenfolge gewährleistet die korrekte Überlagerungsreihenfolge der grafischen Schichten.
onDraw wird immer vor dispatchDraw ausgeführt. Das bedeutet, dass der Inhalt der Eltern-View unter den Child-Views angezeigt wird. Wenn etwas über den Kindern gezeichnet werden muss, geschieht dies in onDrawForeground oder in einer überschriebenen dispatchDraw-Methode durch Aufruf von super.dispatchDraw(canvas) und anschließendem Darüberzeichnen. Der Hintergrund wird noch vor onDraw gezeichnet — in drawBackground(canvas) innerhalb der draw()-Methode.
Bei Hardwarebeschleunigung arbeitet die Zeichen-Pipeline über die GPU. Die View wird in eine DisplayList gezeichnet — eine Liste von Zeichenbefehlen, die zwischengespeichert und wiederverwendet wird. dispatchDraw fügt im hardwarebeschleunigten Modus die DisplayLists der Child-Views zur gesamten DisplayList der Szene hinzu. Das Ändern von dispatchDraw kann zur Ungültigmachung der DisplayList und zum Neuaufbau des Caches führen, was die Leistung beeinträchtigt.
| Methode | Aufrufreihenfolge | Zweck |
|---|---|---|
| drawBackground | 1 | Zeichnen des Hintergrunds (background drawable) |
| onDraw | 2 | Zeichnen des View-eigenen Inhalts |
| dispatchDraw | 3 | Zeichnen aller Child-Views (nur ViewGroup) |
| onDrawForeground | 4 | Zeichnen von Vordergrund, Bildlaufleiste und Ripple-Effekt |
onDraw ist für das Zeichnen des View-eigenen Inhalts verantwortlich — Formen, Text, Bilder, die zu diesem spezifischen Element gehören. dispatchDraw ist für das Zeichnen der Child-Views verantwortlich — aller Elemente, die sich innerhalb der ViewGroup befinden. Wenn eine ViewGroup dispatchDraw nicht überschreibt, wird die Implementierung aus ViewGroup verwendet, die einfach rekursiv childCount durchläuft und draw für jedes Kind aufruft.
Für eine ViewGroup, die keinen eigenen Inhalt zeichnet (z. B. FrameLayout, LinearLayout), kann onDraw durch Setzen von setWillNotDraw(true) optimiert werden. In diesem Fall wird onDraw nie aufgerufen, was Ressourcen spart. dispatchDraw funktioniert weiterhin und ruft draw für die Kindelemente auf. Alle Standard-ViewGroups (LinearLayout, RelativeLayout, ConstraintLayout) verwenden setWillNotDraw(true).
Wenn Sie dispatchDraw ohne Aufruf von super.dispatchDraw(canvas) überschreiben, werden die Child-Views nicht gezeichnet. Dies kann zum vorübergehenden Ausblenden aller Kinder nützlich sein, verursacht aber in den meisten Fällen Fehler. Empfohlene Vorgehensweise: Rufen Sie super.dispatchDraw(canvas) am Anfang der überschriebenen Methode auf und fügen Sie dann Ihre eigene Grafik (z. B. ein Overlay mit Alphakanal) über den Kindern hinzu.
Erstellen wir eine OverlayViewGroup — eine benutzerdefinierte ViewGroup, die über allen Child-Views eine halbtransparente Schicht mit einer Textmarkierung hinzufügt. dispatchDraw ruft zunächst super auf (zeichnet alle Kinder), zeichnet dann das Overlay-Rechteck und den Text. Paint wird im Konstruktor erstellt, um Allokationen in der draw-Schleife zu vermeiden.
class OverlayViewGroup(context: Context)
: ViewGroup(context) {
private val overlayPaint = Paint().apply {
color = Color.parseColor("#66000000")
style = Paint.Style.FILL
}
private val textPaint = Paint().apply {
color = Color.WHITE
textSize = 36f
isAntiAlias = true
textAlign = Paint.Align.CENTER
}
init {
setWillNotDraw(false)
}
override fun dispatchDraw(canvas: Canvas) {
super.dispatchDraw(canvas)
canvas.drawRect(0f, 0f,
width.toFloat(),
height.toFloat(), overlayPaint)
canvas.drawText("VORSCHAU-MODUS",
width / 2f,
height / 2f, textPaint)
}
override fun onLayout(changed: Boolean,
l: Int, t: Int,
r: Int, b: Int) {
var top = t
for (i in 0 until childCount) {
val child = getChildAt(i)
val cw = child.measuredWidth
val ch = child.measuredHeight
child.layout(l, top, l + cw, top + ch)
top += ch
}
}
override fun onMeasure(widthMeasureSpec: Int,
heightMeasureSpec: Int) {
measureChildren(widthMeasureSpec, heightMeasureSpec)
val maxWidth = resolveSize(
getChildAt(0).measuredWidth,
widthMeasureSpec)
var totalHeight = 0
for (i in 0 until childCount) {
totalHeight += getChildAt(i).measuredHeight
}
setMeasuredDimension(maxWidth,
resolveSize(totalHeight, heightMeasureSpec))
}
}
Das folgende Beispiel demonstriert das Ändern der Reihenfolge des Zeichnens von Kindelementen. CircularRevealLayout überschreibt dispatchDraw und zeichnet die Kinder in umgekehrter Reihenfolge, wodurch ein umgekehrter Z-Order-Effekt entsteht. Für die Animation wird basierend auf dem animierten Offset-Wert eine zyklische Verschiebung des Zeichenindex hinzugefügt.
class ReverseOrderLayout(context: Context)
: ViewGroup(context) {
private var reverse = false
override fun dispatchDraw(canvas: Canvas) {
if (!reverse) {
super.dispatchDraw(canvas)
return
}
for (i in childCount - 1 downTo 0) {
val child = getChildAt(i)
if (child.visibility == GONE) continue
drawChild(canvas, child, getDrawingTime())
}
}
fun toggleReverse() {
reverse = !reverse
invalidate()
}
override fun onLayout(changed: Boolean,
l: Int, t: Int,
r: Int, b: Int) {
var left = l
for (i in 0 until childCount) {
val child = getChildAt(i)
val cw = child.measuredWidth
child.layout(left, t,
left + cw, t + child.measuredHeight)
left += cw
}
}
override fun onMeasure(widthMeasureSpec: Int,
heightMeasureSpec: Int) {
measureChildren(widthMeasureSpec, heightMeasureSpec)
var totalWidth = 0
for (i in 0 until childCount) {
totalWidth += getChildAt(i).measuredWidth
}
setMeasuredDimension(
resolveSize(totalWidth, widthMeasureSpec),
resolveSize(getChildAt(0).measuredHeight,
heightMeasureSpec))
}
}
dispatchDraw wird in Szenarien verwendet, in denen Sie den Zeichenprozess der Child-Views beeinflussen müssen, ohne den eigenen Inhalt der ViewGroup zu beeinträchtigen. Hauptszenarien: Anwenden eines gemeinsamen Effekts (Overlay, Schatten, Verlauf) über allen Kindern; Ändern des Z-Order zum Erzeugen von Tiefeneffekten; Animieren des Erscheinens oder Verschwindens von Kindelementen durch Canvas-Transformationen.
Wenn Sie Grafiken unterhalb der Kindelemente anwenden müssen (Hintergrundeffekt), verwenden Sie onDraw — dispatchDraw wird danach aufgerufen. Wenn Grafiken über den Kindern liegen sollen — verwenden Sie dispatchDraw mit vorherigem super-Aufruf, dann Canvas.draw. Wenn Sie eine globale Farb- oder Transparenzänderung benötigen, verwenden Sie canvas.saveLayerAlpha() in dispatchDraw, um das Zeichnen der Kinder zu umschließen.
Nicht empfohlen wird dispatchDraw für: 1) Zeichnen komplexer Animationen in Echtzeit (verwenden Sie invalidate und onDraw); 2) Erstellen von Screenshots einer ViewGroup (verwenden Sie buildDrawingCache() oder View.draw(Canvas)); 3) Ändern von Child-Views (Koordinaten, Größen — das sind Aufgaben von onLayout, nicht von dispatchDraw). dispatchDraw dient nur zum Anwenden visueller Effekte.
Häufig gestellte Fragen
Nein, dispatchDraw ist eine protected-Methode, die vom Android-System innerhalb der öffentlichen draw()-Methode aufgerufen wird. Ein direkter Aufruf von dispatchDraw ist sinnlos, da er keine vorbereitenden Aktionen (Speichern des Canvas, Zeichnen von Hintergrund und Vordergrund) ausführt. Verwenden Sie stattdessen View.draw(Canvas) zum programmatischen Zeichnen auf einen beliebigen Canvas.
Wenn Sie super.dispatchDraw(canvas) nach Ihren eigenen Zeichenoperationen aufrufen, werden die Child-Views über der benutzerdefinierten Grafik gezeichnet. Dies ändert die Schichtreihenfolge: Zuerst wird die benutzerdefinierte Schicht gezeichnet, dann die Kinder. Wenn Sie die umgekehrte Situation benötigen (Grafik über den Kindern), rufen Sie zuerst super.dispatchDraw auf, dann Ihre eigenen Operationen.
dispatchDraw ruft draw() für jede Child-View auf, die Gesamtzeit ist proportional zur Anzahl der Kinder. Bei Hardwarebeschleunigung kann das Hinzufügen von Canvas-Operationen in dispatchDraw die DisplayList ungültig machen und einen Cache-Neuaufbau verursachen. Für 5–10 Kinder ist die Auswirkung minimal, für 50+ Kinder wird empfohlen, komplexe Überlagerungen zwischenzuspeichern.
Eine View (keine ViewGroup) hat keine Kindelemente, daher führt dispatchDraw keine nützliche Arbeit aus. Die Methode existiert jedoch in der Basisklasse View aus Polymorphiegründen: Der Code in draw() ruft dispatchDraw für jede View auf, aber die View-Implementierung enthält keine Logik zum Zeichnen von Kindern. Nur ViewGroup überschreibt dispatchDraw.
Ja, dispatchDraw akzeptiert einen Canvas, der vor dem Aufruf von super.dispatchDraw transformiert werden kann (translate, rotate, scale). Dies wird verwendet, um die gesamte Kindermenge als Einheit zu animieren. Nach Abschluss von super.dispatchDraw wird empfohlen, den Canvas in seinen ursprünglichen Zustand zurückzuversetzen.
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