Canvas in Android — è una classe del pacchetto android.graphics che fornisce un’API per il rendering 2D su View. Canvas funziona come una tela su cui lo sviluppatore disegna forme geometriche, testo, immagini e percorsi utilizzando vari metodi draw*. Secondo la Documentazione per Sviluppatori Android (2025), Canvas è lo strumento principale per il rendering personalizzato in Android e viene utilizzato insieme alla classe Paint per definire stili e colori. Ogni chiamata onDraw() riceve un’istanza Canvas pronta, associata al buffer bitmap della view.
Punti Chiave
Canvas — è la classe centrale della grafica 2D in Android che fornisce metodi per disegnare su un piano. Ogni Canvas è collegato a un Bitmap — un’area di memoria in cui vengono registrati i risultati delle operazioni di disegno. Quando una View viene ridisegnata, il sistema passa a onDraw() un Canvas già associato al buffer di quella View.
Canvas funziona in un sistema di coordinate in cui (0,0) è l’angolo in alto a sinistra, l’asse X va verso destra, l’asse Y verso il basso. Tutte le operazioni di disegno utilizzano lo stato corrente di Canvas, inclusa la matrice di trasformazione, l’area di ritaglio e lo stile Paint. Secondo la Documentazione Android (2025), l’API Canvas include oltre 30 metodi draw* per diversi tipi di primitive grafiche.
Da Android 3.0 (API 11) Canvas supporta l’accelerazione hardware tramite DisplayList. In modalità accelerata, le operazioni Canvas vengono registrate in una DisplayList e poi eseguite sulla GPU. Questo offre un significativo aumento delle prestazioni ma impone limitazioni: alcune operazioni Canvas (clipPath con clip non Path) non sono supportate in modalità accelerata.
Canvas fornisce un insieme di metodi per disegnare primitive di base. Ogni metodo accetta parametri geometrici e un oggetto Paint che definisce lo stile visivo.
| Metodo | Scopo | Esempio |
|---|---|---|
| drawRect() | Disegna un rettangolo | canvas.drawRect(10f, 10f, 100f, 100f, paint) |
| drawCircle() | Disegna un cerchio | canvas.drawCircle(50f, 50f, 30f, paint) |
| drawLine() | Disegna una linea | canvas.drawLine(0f, 0f, 100f, 100f, paint) |
| drawText() | Disegna testo | canvas.drawText(“Hello”, 10f, 20f, paint) |
| drawBitmap() | Disegna un’immagine | canvas.drawBitmap(bitmap, 0f, 0f, paint) |
| drawPath() | Disegna un percorso arbitrario | canvas.drawPath(path, paint) |
| drawOval() | Disegna un ovale | canvas.drawOval(rectF, paint) |
| drawArc() | Disegna un arco o settore | canvas.drawArc(rectF, 0f, 90f, true, paint) |
Ogni metodo può essere chiamato in qualsiasi ordine — Canvas disegna nell’ordine delle chiamate, sovrapponendo nuovi elementi sopra quelli precedenti. Per composizioni complesse, viene utilizzata la tecnica di rendering multistrato con salvataggio e ripristino dello stato di Canvas.
Paint — è la classe che definisce come Canvas disegnerà. Senza Paint, i metodi Canvas sono inutili — imposta colore, spessore della linea, stile di riempimento, dimensione del testo, effetti (ombra, gradiente, sfocatura) e modalità di fusione.
Paint ha proprietà chiave: color (colore), style (FILL, STROKE o FILL_AND_STROKE), strokeWidth (spessore del tratto), textSize (dimensione del testo), isAntiAlias (antialiasing), shader (gradiente o immagine). Creare un oggetto Paint è un’operazione costosa, quindi dovrebbe essere creato una volta e riutilizzato.
private val fillPaint = Paint().apply {
color = Color.parseColor("#FF6200EE")
style = Paint.Style.FILL
isAntiAlias = true
}
private val strokePaint = Paint().apply {
color = Color.WHITE
style = Paint.Style.STROKE
strokeWidth = 4f
isAntiAlias = true
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
canvas.drawCircle(100f, 100f, 80f, fillPaint)
canvas.drawCircle(100f, 100f, 80f, strokePaint)
}
In questo esempio, vengono utilizzati due oggetti Paint: uno per riempire il cerchio di viola, il secondo per il tratto bianco. Separare riempimento e tratto è una pratica standard che consente un controllo flessibile dell’aspetto senza ricreare oggetti.
Per i riempimenti sfumati, Paint accetta uno Shader — le sottoclassi LinearGradient, RadialGradient, SweepGradient. Shader viene collegato a Paint tramite il metodo setShader() e definisce come il colore cambia nell’area di disegno.
Canvas supporta trasformazioni matriciali del sistema di coordinate: translate() (traslazione), rotate() (rotazione), scale() (scala) e skew() (inclinazione). Le trasformazioni si accumulano: ogni chiamata successiva viene applicata sopra le precedenti.
La gestione dello stack di trasformazioni avviene tramite save() e restore(). save() salva lo stato corrente di Canvas (matrice, ritaglio) nello stack, restore() ripristina l’ultimo stato salvato. Tra save() e restore() possono essere applicate trasformazioni annidate.
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
canvas.save() // Save initial state
canvas.translate(100f, 100f) // Translate origin
// First square
canvas.drawRect(0f, 0f, 50f, 50f, fillPaint)
canvas.save() // Save after translate
canvas.rotate(45f, 25f, 25f) // Rotate around square center
// Rotated square
canvas.drawRect(0f, 0f, 50f, 50f, strokePaint)
canvas.restore() // Restore after rotate
canvas.restore() // Restore initial state
}
Utilizzare sempre save() prima di iniziare le trasformazioni e restore() dopo averle completate. Le coppie save/restore sbilanciate portano all’accumulo della matrice e al rendering errato degli elementi successivi.
Per la grafica complessa, Canvas viene combinato con Bitmap e Path. Bitmap viene utilizzato per caricare immagini e memorizzare nella cache i frame renderizzati. Path viene utilizzato per costruire curve di Bezier arbitrari, poligoni e contorni complessi.
La tecnica di rendering offscreen: viene creato un Bitmap, una scena complessa viene disegnata su di esso tramite un Canvas separato, e poi il Bitmap finito viene visualizzato in onDraw() tramite drawBitmap(). Ciò consente di aggiornare grafica complessa senza ridisegnare completamente ogni frame.
private var offscreenBitmap: Bitmap? = null
private var offscreenCanvas: Canvas? = null
fun buildOffscreenScene(width: Int, height: Int) {
offscreenBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
offscreenCanvas = Canvas(offscreenBitmap)
// Render scene once
offscreenCanvas!!.drawColor(Color.WHITE)
drawGrid(offscreenCanvas!!)
drawComplexShape(offscreenCanvas!!)
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
offscreenBitmap?.let { canvas.drawBitmap(it, 0f, 0f, null) }
}
Path viene utilizzato per costruire contorni complessi: curve di Bezier (quadTo, cubicTo), archi (arcTo), linee (lineTo). Path può essere chiuso (close()) per forme riempite o aperto per linee. Il metodo principale drawPath() combina Path e Paint per il rendering finale.
Le prestazioni del rendering di Canvas influenzano direttamente gli FPS dell’applicazione. I problemi principali: allocazioni eccessive in onDraw(), chiamate frequenti a invalidate() e uso subottimale dell’Accelerazione Hardware.
Paint, Path, Rect, RectF e altri oggetti dovrebbero essere creati una volta nell’inizializzatore o nel metodo setup(). Ogni allocazione in onDraw() crea pressione sul garbage collector, causando micro-ritardi. L’eccezione sono gli oggetti static o companion object per oggetti immutabili.
Se la maggior parte della View è nascosta al di fuori dell’area visibile (ad esempio, in ScrollView), utilizzare canvas.clipRect() per limitare il rendering. L’Accelerazione Hardware in questo caso ottimizza ulteriormente il ritaglio dei pixel invisibili.
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
// Clip to visible area
canvas.save()
canvas.clipRect(scrollX, scrollY, scrollX + width, scrollY + height)
// Render large scene
renderScene(canvas)
canvas.restore()
}
Utilizzare View.setLayerType() con LAYER_TYPE_HARDWARE per Views con grafica Canvas complessa che vengono aggiornate raramente. Per animazioni aggiornate frequentemente, utilizzare invece LAYER_TYPE_NONE per evitare costosi aggiornamenti del layer hardware. Eseguire il profiling con GPU Profile Rendering e Android Studio Profiler.
Domande Frequenti
Sì, è possibile creare Canvas(Bitmap) — un costruttore che accetta un Bitmap. Ciò viene utilizzato per il rendering offscreen: si disegna su un Bitmap e poi lo si visualizza tramite drawBitmap(). Canvas può anche essere creato da Surface (per SurfaceView) o tramite Picture.
Canvas — è un’API per disegnare su View nel thread UI. SurfaceView — è una Surface separata che può essere aggiornata da un thread in background tramite il proprio Canvas (lockCanvas() / unlockCanvasAndPost()). SurfaceView è preferibile per giochi e video.
Il motivo più comune sono le coppie sbilanciate di save() e restore(). Se dopo una trasformazione (translate, rotate) restore() non viene chiamato, Canvas rimane in un sistema di coordinate modificato e gli elementi successivi vengono disegnati con uno scostamento. Verificare le proprie coppie save()/restore().
Utilizzare Paint.setTextAlign(Paint.Align.CENTER) per il centraggio orizzontale e Paint.getTextBounds() o Paint.descent() / ascent() per quello verticale. Centraggio: canvas.drawText(testo, centerX, centerY - (ascent + descent) / 2, paint).
Canvas di per sé non anima — disegna solo lo stato corrente. L’animazione si ottiene tramite chiamate successive a invalidate() (o postInvalidateOnAnimation()), disegnando ogni volta un nuovo frame in onDraw(). Per animazioni fluide, utilizzare ValueAnimator o Choreographer per sincronizzarsi con Vsync.
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