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 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.
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.
| Metodo | Ordine di chiamata | Scopo |
|---|---|---|
| drawBackground | 1 | Disegno dello sfondo (background drawable) |
| onDraw | 2 | Disegno del contenuto della View stessa |
| dispatchDraw | 3 | Disegno di tutte le View figlie (solo ViewGroup) |
| onDrawForeground | 4 | Disegno di primo piano, barra di scorrimento e effetto ondulazione |
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.
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.
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))
}
}
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.
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 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
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.
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.
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.
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.
Sì, 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
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.
Leggi anche