dispatchDraw(): wat is het, aanroepmechanisme en verschil met onDraw

Auteur: IT Sectr Gepubliceerd: 2026-07-22 Leestijd: 9 min

dispatchDraw() — is een methode van de klasse ViewGroup die verantwoordelijk is voor het recursief tekenen van alle kind-Views in de Android-hiërarchie. Volgens de Android Developers Documentation (2026) wordt dispatchDraw automatisch aangeroepen na onDraw in de draw()-methode van de hoofd-View en doorloopt het alle kindelementen, waarbij hun eigen draw() wordt aangeroepen. Ontwikkelaars overschrijven dispatchDraw in aangepaste ViewGroups om effecten toe te voegen, graphics over kinderen heen te leggen of de tekenvolgorde te wijzigen.

Belangrijkste

  • dispatchDraw(Canvas canvas) — protected methode van ViewGroup, aangeroepen na onDraw voor het tekenen van kind-Views
  • draw(Canvas canvas) — public final methode van View die achtereenvolgens onDraw, dispatchDraw en onDrawForeground aanroept
  • onDraw tekent de inhoud van de View zelf, dispatchDraw — de inhoud van alle kindelementen
  • setWillNotDraw(true) — optimalisatie voor ViewGroup die geen eigen inhoud tekent, alleen kindelementen
  • Het overschrijven van dispatchDraw wordt gebruikt voor het toepassen van effecten (blur, overlay) of animatie van het tekenen van kindelementen

Wat is dispatchDraw()?

dispatchDraw(Canvas canvas) — is een protected methode van de klasse ViewGroup die het Android-systeem aanroept om alle kind-Views van de huidige container te tekenen. dispatchDraw maakt deel uit van de standaard draw-pijplijn: eerst wordt onDraw uitgevoerd (tekenen van de inhoud van de View zelf), daarna dispatchDraw (recursief tekenen van kinderen), dan onDrawForeground (tekenen van foreground en scrollbars). De methode is niet bedoeld om direct vanuit applicatiecode te worden aangeroepen.

De standaardimplementatie van dispatchDraw in ViewGroup doorloopt alle kind-Views, controleert hun zichtbaarheid en roept voor elk draw(Canvas) aan. De volgorde van doorlopen komt overeen met de indices van de kinderen (van 0 tot childCount - 1). Als kind-Views elkaar overlappen, worden de laatste op de lijst over de eerste getekend. Door de volgorde in dispatchDraw te wijzigen, kan de Z-order van het tekenen worden aangepast.

Voor View (niet ViewGroup) is de dispatchDraw-methode leeg — een gewone View heeft geen kindelementen, dus er is niets om te tekenen. De methode bestaat in de basisklasse View, maar wordt alleen aangeroepen voor ViewGroup-instanties. De ontwikkelaar kan controleren of een View kindelementen heeft via het standaardgedrag van dispatchDraw, maar het is vaak eenvoudiger om instanceof ViewGroup te controleren.

Tekenpijplijn: onDraw, dispatchDraw, onDrawForeground

De methode draw(Canvas) van de klasse View organiseert een driefasige tekenpijplijn. De eerste fase — aanroep van onDraw(canvas), waarin de View zijn inhoud tekent. De tweede fase — aanroep van dispatchDraw(canvas), die het tekenen van kind-Views start. De derde fase — onDrawForeground(canvas), verantwoordelijk voor de foreground-laag, scrollbars en ripple-effecten. Deze volgorde garandeert de juiste stapeling van grafische lagen.

onDraw wordt altijd uitgevoerd vóór dispatchDraw. Dit betekent dat de inhoud van de bovenliggende View onder de kind-Views wordt weergegeven. Als er iets over kinderen heen moet worden getekend, gebeurt dit in onDrawForeground of in een overschreven dispatchDraw met aanroep van super.dispatchDraw(canvas) en daaropvolgend tekenen eroverheen. De achtergrond (background) wordt nog vóór onDraw getekend — in drawBackground(canvas) binnen de methode draw().

Bij hardwareversnelling (hardware acceleration) werkt de draw-pijplijn via de GPU. De View wordt getekend in een DisplayList — een lijst met tekenopdrachten die wordt gecachet en hergebruikt. dispatchDraw in hardwareversnelde modus voegt de DisplayList van kind-Views toe aan de algemene DisplayList van de scène. Het wijzigen van dispatchDraw kan leiden tot invalidatie van de DisplayList en herbouw van de cache, wat de prestaties beïnvloedt.

MethodeAanroepvolgordeDoel
drawBackground1Tekenen van de achtergrond (background drawable)
onDraw2Tekenen van de inhoud van de View zelf
dispatchDraw3Tekenen van alle kind-Views (alleen voor ViewGroup)
onDrawForeground4Tekenen van foreground, scrollbar en ripple-effect

Verschil tussen dispatchDraw en onDraw

onDraw is verantwoordelijk voor het tekenen van de inhoud van de View zelf — vormen, tekst, afbeeldingen die bij dit specifieke element horen. dispatchDraw is verantwoordelijk voor het tekenen van kind-Views — alle elementen die zich binnen de ViewGroup bevinden. Als ViewGroup dispatchDraw niet overschrijft, wordt de implementatie uit ViewGroup gebruikt, die gewoon recursief door childCount loopt en draw voor elk kind aanroept.

Voor ViewGroup die geen eigen inhoud tekent (bijv. FrameLayout, LinearLayout), kan onDraw worden geoptimaliseerd door setWillNotDraw(true) in te stellen. In dit geval wordt onDraw nooit aangeroepen, wat resources bespaart. dispatchDraw blijft ondertussen werken en draw aanroepen voor kindelementen. Alle standaard ViewGroups (LinearLayout, RelativeLayout, ConstraintLayout) gebruiken setWillNotDraw(true).

Als dispatchDraw wordt overschreven zonder super.dispatchDraw(canvas) aan te roepen, worden kind-Views niet getekend. Dit kan nuttig zijn voor het tijdelijk verbergen van alle kinderen, maar veroorzaakt in de meeste gevallen fouten. Aanbevolen praktijk: roep super.dispatchDraw(canvas) aan het begin van de overschreven methode aan en voeg dan uw eigen graphics (bijv. overlay met alfakanaal) over de kinderen toe.

Voorbeeld van het overschrijven van dispatchDraw in Kotlin

Laten we OverlayViewGroup maken — een aangepaste ViewGroup die een halfdoorzichtige laag met een tekstlabel over alle kind-Views toevoegt. dispatchDraw roept eerst super aan (tekenen van alle kinderen), tekent dan de overlay-rechthoek en tekst. Paint wordt in de constructor gemaakt om allocaties in de draw-lus te voorkomen.

kotlin
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("VOORVERTOONMODUS",
            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))
    }
}

Geanimeerde Z-order via dispatchDraw

Het volgende voorbeeld toont het wijzigen van de volgorde van het tekenen van kindelementen. CircularRevealLayout overschrijft dispatchDraw en tekent de kinderen in omgekeerde volgorde, waardoor een effect van omgekeerde Z-order ontstaat. Voor animatie wordt een cyclische verschuiving van de tekenindex toegevoegd op basis van de geanimeerde offset-waarde.

kotlin
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))
    }
}

Wanneer dispatchDraw gebruiken in Android

dispatchDraw wordt gebruikt in scenario's waarbij u het tekenproces van kind-Views moet beïnvloeden zonder de eigen inhoud van de ViewGroup aan te tasten. Belangrijkste scenario's: het toepassen van een gemeenschappelijk effect (overlay, shadow, gradient) over alle kinderen; het wijzigen van de Z-order-volgorde voor een diepte-effect; het animeren van het verschijnen of verdwijnen van kindelementen via Canvas-transformaties.

Als graphics onder kindelementen moeten worden geplaatst (achtergrond-effect), moet onDraw worden gebruikt — dispatchDraw wordt daarna aangeroepen. Als graphics over kinderen heen moeten worden geplaatst — gebruik dispatchDraw met super-aanroep en vervolgens Canvas.draw. Als een globale kleur- of transparantiewijziging nodig is, is het handig om canvas.saveLayerAlpha() in dispatchDraw te gebruiken, waarmee het tekenen van kinderen wordt omhuld.

Wordt niet aanbevolen om dispatchDraw te gebruiken voor: 1) het tekenen van complexe animaties in realtime (gebruik invalidate en onDraw); 2) het maken van screenshots van ViewGroup (gebruik buildDrawingCache() of View.draw(Canvas)); 3) het wijzigen van kind-Views (coördinaten, afmetingen — dit is de taak van onLayout, niet dispatchDraw). dispatchDraw — alleen voor het toepassen van visuele effecten.

Veelgestelde vragen

Kan dispatchDraw direct zonder systeem worden aangeroepen?

Nee, dispatchDraw — protected methode aangeroepen door het Android-systeem binnen de publieke methode draw(). Directe aanroep van dispatchDraw heeft geen zin, omdat het geen voorbereidende acties uitvoert (Canvas opslaan, achtergrond en foreground tekenen). Gebruik in plaats daarvan View.draw(Canvas) voor programmatisch tekenen in een willekeurige Canvas.

Wat gebeurt er bij aanroep van super.dispatchDraw na aangepast tekenen?

Als u super.dispatchDraw(canvas) aanroept na uw eigen tekenbewerkingen, worden kind-Views over de aangepaste graphics getekend. Dit verandert de laagstapeling: eerst wordt de aangepaste laag getekend, dan de kinderen. Als de omgekeerde situatie nodig is (graphics over kinderen), roep dan eerst super.dispatchDraw aan en dan uw eigen bewerkingen.

Hoe beïnvloedt dispatchDraw de prestaties?

dispatchDraw roept draw() aan voor elk kind-View, de totale tijd is evenredig met het aantal kinderen. Bij hardwareversnelling kan het toevoegen van Canvas-bewerkingen in dispatchDraw leiden tot invalidatie van de DisplayList en herbouw van de cache. Voor 5–10 kinderen is de impact minimaal, voor 50+ kinderen wordt het cachen van complexe overlays aanbevolen.

Waarom wordt dispatchDraw niet aangeroepen voor View?

View (niet ViewGroup) heeft geen kindelementen, dus dispatchDraw voert geen nuttig werk uit. De methode bestaat echter in de basisklasse View voor polymorfisme: de code in draw() roept dispatchDraw aan voor elke View, maar de implementatie van View bevat geen logica voor het tekenen van kinderen. Alleen ViewGroup overschrijft dispatchDraw.

Kan Canvas worden gewijzigd in dispatchDraw?

Ja, dispatchDraw ontvangt een Canvas en kan worden getransformeerd vóór aanroep van super.dispatchDraw (translate, rotate, scale). Dit wordt toegepast voor het animeren van de hele set kinderen als één geheel. Na voltooiing van super.dispatchDraw wordt aanbevolen de Canvas te herstellen naar de oorspronkelijke staat.

Samenvatting

  • dispatchDraw(Canvas) — ViewGroup-methode voor recursief tekenen van alle kind-Views na onDraw
  • Draw-pijplijn: drawBackground → onDraw → dispatchDraw → onDrawForeground
  • onDraw tekent de inhoud van de View zelf, dispatchDraw — de inhoud van kindelementen
  • setWillNotDraw(true) optimaliseert ViewGroup, slaat onDraw over, maar dispatchDraw blijft werken
  • Het overschrijven van dispatchDraw wordt gebruikt voor het toepassen van effecten, wijzigen van Z-order en Canvas-transformaties van kinderen
  • De volgorde van super.dispatchDraw en aangepast tekenen bepaalt wat er over de kinderen komt
  • Voor gewone View (niet ViewGroup) is dispatchDraw leeg — deze heeft geen kindelementen

We ontwikkelen een mobiele applicatie turnkey

IT Sectr creëert sinds 2017 iOS- en Android-applicaties voor startups en bedrijven. We adviseren u en stellen de beste oplossing voor.

Bespreek het project