dispatchDraw(): vad är det, anropsmekanism och skillnad från onDraw

Författare: IT Sectr Publicerad: 2026-07-22 Lästid: 9 min

dispatchDraw() — är en metod i klassen ViewGroup som ansvarar för rekursiv ritning av alla barn-View i Android-hierarkin. Enligt Android Developers Documentation (2026) anropas dispatchDraw automatiskt efter onDraw i draw()-metoden för huvud-View och går igenom alla barnelement och anropar deras egen draw(). Utvecklare åsidosätter dispatchDraw i anpassade ViewGroup för att lägga till effekter, lägga grafik över barn eller ändra ritningsordningen.

Huvudsakligt

  • dispatchDraw(Canvas canvas) — protected metod i ViewGroup anropad efter onDraw för att rita barn-View
  • draw(Canvas canvas) — public final metod i View som i tur och ordning anropar onDraw, dispatchDraw och onDrawForeground
  • onDraw ritar innehållet i själva View, dispatchDraw — innehållet i alla barnelement
  • setWillNotDraw(true) — optimering för ViewGroup som inte ritar eget innehåll, endast barnelement
  • Åsidosättning av dispatchDraw används för att applicera effekter (blur, overlay) eller animera ritning av barnelement

Vad är dispatchDraw()?

dispatchDraw(Canvas canvas) — är en protected metod i klassen ViewGroup som Android-systemet anropar för att rita alla barn-View i den aktuella containern. dispatchDraw är en del av standardritningspipelinen: först körs onDraw (ritning av själva View:s innehåll), sedan dispatchDraw (rekursiv ritning av barn), sedan onDrawForeground (ritning av foreground och scrollbars). Metoden är inte avsedd att anropas direkt från applikationskoden.

Standardimplementeringen av dispatchDraw i ViewGroup itererar genom alla barn-View, kontrollerar deras synlighet och anropar för varje draw(Canvas). Genomgångsordningen motsvarar barnens index (från 0 till childCount - 1). Om barn-View överlappar varandra, ritas de sista i listan över de första. Genom att ändra ordningen i dispatchDraw kan Z-order för ritning ändras.

För View (inte ViewGroup) är dispatchDraw-metoden tom — en vanlig View har inga barnelement, så det finns inget att rita. Metoden finns i basklassen View, men anropas endast för ViewGroup-instanser. Utvecklaren kan kontrollera om View har barnelement via dispatchDraw:s standardbeteende, men det är ofta enklare att kontrollera instanceof ViewGroup.

Ritningspipeline: onDraw, dispatchDraw, onDrawForeground

Metoden draw(Canvas) i klassen View organiserar en trestegs ritningspipeline. Första steget — anrop av onDraw(canvas), där View ritar sitt innehåll. Andra steget — anrop av dispatchDraw(canvas), som startar ritning av barn-View. Tredje steget — onDrawForeground(canvas), ansvarig för foreground-lager, scrollbars och ripple-effekter. Denna sekvens garanterar korrekt ordning för överlappning av grafiska lager.

onDraw körs alltid före dispatchDraw. Detta innebär att innehållet i den överordnade View visas under barn-View. Om något behöver ritas ovanpå barnen görs detta i onDrawForeground eller i en åsidosatt dispatchDraw med anrop av super.dispatchDraw(canvas) och efterföljande ritning ovanpå. Bakgrunden (background) ritas redan före onDraw — i drawBackground(canvas) inuti metoden draw().

Vid hårdvaruacceleration (hardware acceleration) fungerar ritningspipelinen via GPU. View ritas i en DisplayList — en lista med ritkommandon som cachas och återanvänds. dispatchDraw i hårdvaruaccelererat läge lägger till barn-View:s DisplayList till scenens allmänna DisplayList. Ändring av dispatchDraw kan leda till ogiltigförklaring av DisplayList och återuppbyggnad av cachen, vilket påverkar prestanda.

MetodAnropsordningSyfte
drawBackground1Ritning av bakgrunden (background drawable)
onDraw2Ritning av själva View:s innehåll
dispatchDraw3Ritning av alla barn-View (endast för ViewGroup)
onDrawForeground4Ritning av foreground, scrollbar och ripple-effekt

Skillnad mellan dispatchDraw och onDraw

onDraw ansvarar för att rita innehållet i själva View — former, text, bilder som tillhör detta specifika element. dispatchDraw ansvarar för att rita barn-View — alla element som finns inuti ViewGroup. Om ViewGroup inte åsidosätter dispatchDraw används implementeringen från ViewGroup, som helt enkelt rekursivt går igenom childCount och anropar draw för varje barn.

För ViewGroup som inte ritar eget innehåll (t.ex. FrameLayout, LinearLayout) kan onDraw optimeras genom att ställa in setWillNotDraw(true). I detta fall anropas onDraw aldrig, vilket sparar resurser. dispatchDraw fortsätter att fungera och anropa draw för barnelement. Alla standard ViewGroup (LinearLayout, RelativeLayout, ConstraintLayout) använder setWillNotDraw(true).

Om dispatchDraw åsidosätts utan att anropa super.dispatchDraw(canvas) kommer barn-View inte att ritas. Detta kan vara användbart för tillfällig döljning av alla barn, men orsakar i de flesta fall fel. Rekommenderad praxis: anropa super.dispatchDraw(canvas) i början av den åsidosatta metoden och lägg sedan till din egen grafik (t.ex. overlay med alfakanal) ovanpå barnen.

Exempel på åsidosättning av dispatchDraw i Kotlin

Låt oss skapa OverlayViewGroup — en anpassad ViewGroup som lägger till ett halvgenomskinligt lager med en textetikett ovanpå alla barn-View. dispatchDraw anropar först super (ritning av alla barn), ritar sedan overlay-rektangeln och texten. Paint skapas i konstruktorn för att undvika allokeringar i draw-loopen.

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("FÖRHANDSGRANSKNINGSLÄGE",
            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))
    }
}

Animerad Z-order via dispatchDraw

Följande exempel visar ändring av ordningen för ritning av barnelement. CircularRevealLayout åsidosätter dispatchDraw och ritar barnen i omvänd ordning, vilket skapar en effekt av omvänd Z-order. För animering läggs en cyklisk förskjutning av ritningsindex till baserat på det animerade offset-värdet.

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

När ska dispatchDraw användas i Android

dispatchDraw används i scenarier där du behöver påverka ritningsprocessen för barn-View utan att påverka ViewGroup:s eget innehåll. Huvudscenarier: applicering av en gemensam effekt (overlay, shadow, gradient) ovanpå alla barn; ändring av Z-order-ordning för att skapa en djup-effekt; animering av uppkomst eller försvinnande av barnelement genom Canvas-transformeringar.

Om grafik behöver läggas under barnelement (bakgrundseffekt) ska onDraw användas — dispatchDraw anropas efter det. Om grafiken ska vara ovanpå barnen — använd dispatchDraw med super-anrop och sedan Canvas.draw. Om global färg- eller transparensändring behövs är det bekvämt att använda canvas.saveLayerAlpha() i dispatchDraw som omsluter ritningen av barnen.

Rekommenderas inte att använda dispatchDraw för: 1) ritning av komplex animering i realtid (använd invalidate och onDraw); 2) skapande av skärmdumpar av ViewGroup (använd buildDrawingCache() eller View.draw(Canvas)); 3) modifiering av barn-View (koordinater, storlekar — detta är onLayout:s uppgift, inte dispatchDraw:s). dispatchDraw — endast för applicering av visuella effekter.

Vanliga frågor

Kan dispatchDraw anropas direkt utan systemet?

Nej, dispatchDraw — protected metod som anropas av Android-systemet inuti den publika metoden draw(). Direkt anrop av dispatchDraw är meningslöst eftersom det inte utför förberedande åtgärder (spara Canvas, rita bakgrund och foreground). Använd istället View.draw(Canvas) för programmatisk ritning i en godtycklig Canvas.

Vad händer vid anrop av super.dispatchDraw efter anpassad ritning?

Om du anropar super.dispatchDraw(canvas) efter dina egna ritningsoperationer kommer barn-View att ritas ovanpå den anpassade grafiken. Detta ändrar lageröverlappningen: först ritas det anpassade lagret, sedan barnen. Om den motsatta situationen behövs (grafik ovanpå barnen), anropa först super.dispatchDraw och sedan dina egna operationer.

Hur påverkar dispatchDraw prestanda?

dispatchDraw anropar draw() för varje barn-View, total tid är proportionell mot antalet barn. Vid hårdvaruacceleration kan tillägg av Canvas-operationer i dispatchDraw leda till ogiltigförklaring av DisplayList och återuppbyggnad av cachen. För 5–10 barn är påverkan minimal, för 50+ barn rekommenderas cachning av komplexa överlägg.

Varför anropas inte dispatchDraw för View?

View (inte ViewGroup) har inga barnelement, därför utför dispatchDraw inget användbart arbete. Metoden finns dock i basklassen View för polymorfism: koden i draw() anropar dispatchDraw för vilken View som helst, men implementeringen av View innehåller ingen logik för att rita barn. Endast ViewGroup åsidosätter dispatchDraw.

Kan Canvas ändras i dispatchDraw?

Ja, dispatchDraw tar emot en Canvas och kan transformeras före anrop av super.dispatchDraw (translate, rotate, scale). Detta tillämpas för att animera hela uppsättningen av barn som en enda enhet. Efter slutförd super.dispatchDraw rekommenderas att Canvas återställs till ursprungligt tillstånd.

Sammanfattning

  • dispatchDraw(Canvas) — ViewGroup-metod för rekursiv ritning av alla barn-View efter onDraw
  • Ritningspipeline: drawBackground → onDraw → dispatchDraw → onDrawForeground
  • onDraw ritar innehållet i själva View, dispatchDraw — innehållet i barnelement
  • setWillNotDraw(true) optimerar ViewGroup, hoppar över onDraw, men dispatchDraw fortsätter att fungera
  • Åsidosättning av dispatchDraw används för att applicera effekter, ändra Z-order och Canvas-transformering av barn
  • Ordningen på super.dispatchDraw och anpassad ritning bestämmer vad som kommer ovanpå barnen
  • För vanlig View (inte ViewGroup) är dispatchDraw tom — har inga barnelement

Vi utvecklar en mobil applikation nyckelfärdigt

IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.

Diskutera projektet

Läs också