dispatchDraw(): cos'è, meccanismo di chiamata e differenza da onDraw

Autore: IT Sectr Pubblicato: 2026-07-22 Tempo di lettura: 9 min

dispatchDraw() è un metodo della classe ViewGroup responsabile del disegno ricorsivo di tutte le View figlie nella gerarchia Android. Secondo la documentazione per sviluppatori Android (2026), dispatchDraw viene chiamato automaticamente dopo onDraw nel metodo draw() della View principale e attraversa tutti gli elementi figli, chiamando il loro draw(). Gli sviluppatori sovrascrivono dispatchDraw in ViewGroup personalizzati per aggiungere effetti, sovrapporre grafica sopra i figli o modificare l'ordine di disegno.

Punti chiave

  • dispatchDraw(Canvas canvas) è un metodo protected di ViewGroup chiamato dopo onDraw per disegnare le View figlie
  • draw(Canvas canvas) è un metodo public final di View che chiama sequenzialmente onDraw, dispatchDraw e onDrawForeground
  • onDraw disegna il contenuto della View stessa, mentre dispatchDraw disegna tutti gli elementi figli
  • setWillNotDraw(true) è un'ottimizzazione per ViewGroup che non disegna il proprio contenuto, solo gli elementi figli
  • Sovrascrivere dispatchDraw viene utilizzato per applicare effetti (blur, overlay) o animare il disegno degli elementi figli

Cos'è dispatchDraw()?

dispatchDraw(Canvas canvas) è un metodo protected della classe ViewGroup che il sistema Android chiama per disegnare tutte le View figlie del contenitore corrente. dispatchDraw fa parte della pipeline di disegno standard: prima viene eseguito onDraw (disegno del contenuto della View stessa), poi dispatchDraw (disegno ricorsivo dei figli), poi onDrawForeground (disegno del primo piano e delle barre di scorrimento). Il metodo non è progettato per essere chiamato direttamente dal codice dell'applicazione.

L'implementazione standard di dispatchDraw in ViewGroup attraversa tutte le View figlie, ne verifica la visibilità e chiama draw(Canvas) per ciascuna. L'ordine di attraversamento corrisponde agli indici dei figli (da 0 a childCount - 1). Se le View figlie si sovrappongono, quelle successive nell'elenco vengono disegnate sopra le precedenti. Modificare l'ordine in dispatchDraw consente di modificare lo Z-order del disegno.

Per una View (non ViewGroup), il metodo dispatchDraw è vuoto — una View normale non ha elementi figli, quindi non c'è nulla da disegnare. Il metodo esiste nella classe base View ma viene chiamato solo su istanze di ViewGroup. Uno sviluppatore può verificare se una View ha elementi figli attraverso il comportamento predefinito di dispatchDraw, ma spesso è più semplice controllare instanceof ViewGroup.

Pipeline di disegno: onDraw, dispatchDraw, onDrawForeground

Il metodo draw(Canvas) della classe View organizza una pipeline di disegno a tre fasi. La prima fase è la chiamata a onDraw(canvas), in cui la View disegna il proprio contenuto. La seconda fase è la chiamata a dispatchDraw(canvas), che attiva il disegno delle View figlie. La terza fase è onDrawForeground(canvas), responsabile del primo piano, delle barre di scorrimento e degli effetti di ondulazione. Questa sequenza garantisce il corretto ordine di sovrapposizione degli strati grafici.

onDraw viene sempre eseguito prima di dispatchDraw. Ciò significa che il contenuto della View padre viene visualizzato sotto le View figlie. Se è necessario disegnare qualcosa sopra i figli, questo viene fatto in onDrawForeground o in un dispatchDraw sovrascritto chiamando super.dispatchDraw(canvas) e poi disegnando sopra. Lo sfondo viene disegnato prima ancora di onDraw — in drawBackground(canvas) all'interno del metodo draw().

Con l'accelerazione hardware, la pipeline di disegno funziona tramite GPU. La View viene disegnata in un DisplayList — un elenco di comandi di disegno che viene memorizzato nella cache e riutilizzato. dispatchDraw in modalità accelerata hardware aggiunge i DisplayList delle View figlie al DisplayList complessivo della scena. Modificare dispatchDraw può invalidare il DisplayList e causare la ricostruzione della cache, con un impatto sulle prestazioni.

MetodoOrdine di chiamataScopo
drawBackground1Disegno dello sfondo (background drawable)
onDraw2Disegno del contenuto della View stessa
dispatchDraw3Disegno di tutte le View figlie (solo ViewGroup)
onDrawForeground4Disegno di primo piano, barra di scorrimento e effetto ondulazione

Differenza tra dispatchDraw e onDraw

onDraw è responsabile del disegno del contenuto della View stessa — forme, testo, immagini che appartengono a questo elemento specifico. dispatchDraw è responsabile del disegno delle View figlie — tutti gli elementi contenuti all'interno del ViewGroup. Se un ViewGroup non sovrascrive dispatchDraw, viene utilizzata l'implementazione di ViewGroup, che attraversa semplicemente ricorsivamente childCount e chiama draw per ogni figlio.

Per un ViewGroup che non disegna il proprio contenuto (ad esempio, FrameLayout, LinearLayout), onDraw può essere ottimizzato impostando setWillNotDraw(true). In questo caso, onDraw non viene mai chiamato, risparmiando risorse. dispatchDraw continua a funzionare e chiama draw per gli elementi figli. Tutti i ViewGroup standard (LinearLayout, RelativeLayout, ConstraintLayout) utilizzano setWillNotDraw(true).

Se si sovrascrive dispatchDraw senza chiamare super.dispatchDraw(canvas), le View figlie non verranno disegnate. Questo può essere utile per nascondere temporaneamente tutti i figli, ma nella maggior parte dei casi causa errori. Pratica consigliata: chiamare super.dispatchDraw(canvas) all'inizio del metodo sovrascritto, quindi aggiungere la propria grafica (ad esempio, un overlay con canale alfa) sopra i figli.

Esempio di override di dispatchDraw in Kotlin

Creiamo un OverlayViewGroup — un ViewGroup personalizzato che aggiunge un livello semitrasparente con un'etichetta di testo sopra tutte le View figlie. dispatchDraw chiama prima super (disegnando tutti i figli), poi disegna il rettangolo di overlay e il testo. Paint viene creato nel costruttore per evitare allocazioni nel ciclo di draw.

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("MODALITÀ ANTEPRIMA",
            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))
    }
}

Z-order animato tramite dispatchDraw

L'esempio seguente dimostra la modifica dell'ordine di disegno degli elementi figli. CircularRevealLayout sovrascrive dispatchDraw e disegna i figli in ordine inverso, creando un effetto di Z-order invertito. Per l'animazione, viene aggiunto uno spostamento ciclico dell'indice di disegno basato sul valore animato offset.

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

Quando usare dispatchDraw in Android

dispatchDraw viene utilizzato in scenari in cui è necessario influenzare il processo di disegno delle View figlie senza influenzare il contenuto proprio del ViewGroup. Scenari principali: applicare un effetto comune (overlay, ombra, gradiente) sopra tutti i figli; modificare lo Z-order per creare effetti di profondità; animare la comparsa o scomparsa degli elementi figli attraverso trasformazioni Canvas.

Se è necessario applicare grafica sotto gli elementi figli (effetto di sfondo), utilizzare onDraw — dispatchDraw viene chiamato dopo. Se la grafica deve stare sopra i figli — utilizzare dispatchDraw chiamando prima super, poi Canvas.draw. Se è necessaria una modifica globale del colore o della trasparenza, è conveniente utilizzare canvas.saveLayerAlpha() in dispatchDraw, avvolgendo il disegno dei figli.

Non consigliato utilizzare dispatchDraw per: 1) disegnare animazioni complesse in tempo reale (utilizzare invalidate e onDraw); 2) creare screenshot di un ViewGroup (utilizzare buildDrawingCache() o View.draw(Canvas)); 3) modificare le View figlie (coordinate, dimensioni — sono compiti di onLayout, non di dispatchDraw). dispatchDraw è solo per applicare effetti visivi.

Domande frequenti

Si può chiamare dispatchDraw direttamente senza il sistema?

No, dispatchDraw è un metodo protected chiamato dal sistema Android all'interno del metodo pubblico draw(). Una chiamata diretta a dispatchDraw non ha senso perché non esegue azioni preparatorie (salvare Canvas, disegnare sfondo e primo piano). Invece, utilizzare View.draw(Canvas) per il disegno programmatico su un Canvas arbitrario.

Cosa succede quando si chiama super.dispatchDraw dopo il disegno personalizzato?

Se si chiama super.dispatchDraw(canvas) dopo le proprie operazioni di disegno, le View figlie verranno disegnate sopra la grafica personalizzata. Questo cambia l'ordine degli strati: prima viene disegnato lo strato personalizzato, poi i figli. Se è necessaria la situazione opposta (grafica sopra i figli), chiamare prima super.dispatchDraw, poi le proprie operazioni.

In che modo dispatchDraw influisce sulle prestazioni?

dispatchDraw chiama draw() per ogni View figlia e il tempo totale è proporzionale al numero di figli. Con l'accelerazione hardware, aggiungere operazioni Canvas in dispatchDraw può invalidare il DisplayList e causare la ricostruzione della cache. Per 5–10 figli l'impatto è minimo, per 50+ figli si consiglia di memorizzare nella cache le sovrapposizioni complesse.

Perché dispatchDraw non viene chiamato per una View?

Una View (non ViewGroup) non ha elementi figli, quindi dispatchDraw non esegue alcun lavoro utile. Tuttavia, il metodo esiste nella classe base View per polimorfismo: il codice in draw() chiama dispatchDraw per qualsiasi View, ma l'implementazione di View non contiene logica di disegno dei figli. Solo ViewGroup sovrascrive dispatchDraw.

Si può modificare il Canvas in dispatchDraw?

, dispatchDraw accetta un Canvas e può essere trasformato prima di chiamare super.dispatchDraw (translate, rotate, scale). Questo viene utilizzato per animare l'intero insieme di figli come unità singola. Dopo il completamento di super.dispatchDraw, si consiglia di ripristinare il Canvas allo stato originale.

Riepilogo

  • dispatchDraw(Canvas) è un metodo di ViewGroup per il disegno ricorsivo di tutte le View figlie dopo onDraw
  • Pipeline di disegno: drawBackground → onDraw → dispatchDraw → onDrawForeground
  • onDraw disegna il contenuto della View stessa, dispatchDraw disegna il contenuto degli elementi figli
  • setWillNotDraw(true) ottimizza ViewGroup saltando onDraw, ma dispatchDraw continua a funzionare
  • Sovrascrivere dispatchDraw viene utilizzato per applicare effetti, modificare Z-order e trasformazioni Canvas dei figli
  • L'ordine di super.dispatchDraw e del disegno personalizzato determina cosa appare sopra i figli
  • Per una View normale (non ViewGroup), dispatchDraw è vuoto — non ha elementi figli

Svilupperemo un'applicazione mobile chiavi in mano

IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.

Discuti il progetto

Leggi anche