Canvas în Android — este o clasă din pachetul android.graphics care oferă API pentru desenarea 2D pe View. Canvas funcționează ca o pânză pe care dezvoltatorul desenează figuri geometrice, text, imagini și căi cu ajutorul diferitelor metode draw*. Conform Android Developers Documentation (2025), Canvas este instrumentul principal pentru desenarea personalizată în Android și este folosit împreună cu clasa Paint pentru setarea stilurilor și culorilor. Fiecare apel onDraw() primește o instanță Canvas pregătită, asociată cu buffer-ul bitmap al view-ului.
Principalele puncte
Canvas — este clasa centrală de grafică 2D în Android care oferă metode pentru desenare pe suprafață. Fiecare Canvas este legat de un Bitmap — o zonă de memorie în care se scriu rezultatele operațiilor de desenare. Când View este redesenat, sistemul transmite în onDraw() un Canvas deja asociat cu buffer-ul acestui View.
Canvas funcționează într-un sistem de coordonate unde (0,0) este colțul din stânga sus, axa X merge la dreapta, axa Y în jos. Toate operațiile de desenare folosesc starea curentă a Canvas, inclusiv matricea de transformare, zona de decupare (clip) și stilul Paint. Conform Android Documentation (2025), API-ul Canvas include peste 30 de metode draw* pentru diferite tipuri de primitive grafice.
Începând cu Android 3.0 (API 11) Canvas suportă accelerarea hardware prin DisplayList. În modul accelerat, operațiile Canvas sunt scrise în DisplayList și apoi executate pe GPU. Aceasta oferă o creștere semnificativă a performanței, dar impune limitări: unele operații Canvas (clipPath cu clip non-Path) nu sunt suportate în modul accelerat.
Canvas oferă un set de metode pentru desenarea primitivelor de bază. Fiecare metodă primește parametri geometrici și un obiect Paint care definește stilul vizual.
| Metodă | Scop | Exemplu |
|---|---|---|
| drawRect() | Desenarea unui dreptunghi | canvas.drawRect(10f, 10f, 100f, 100f, paint) |
| drawCircle() | Desenarea unui cerc | canvas.drawCircle(50f, 50f, 30f, paint) |
| drawLine() | Desenarea unei linii | canvas.drawLine(0f, 0f, 100f, 100f, paint) |
| drawText() | Desenarea textului | canvas.drawText("Hello", 10f, 20f, paint) |
| drawBitmap() | Desenarea unei imagini | canvas.drawBitmap(bitmap, 0f, 0f, paint) |
| drawPath() | Desenarea unei căi arbitrare | canvas.drawPath(path, paint) |
| drawOval() | Desenarea unui oval | canvas.drawOval(rectF, paint) |
| drawArc() | Desenarea unui arc sau sector | canvas.drawArc(rectF, 0f, 90f, true, paint) |
Fiecare metodă poate fi apelată în orice ordine — Canvas desenează în ordinea apelurilor, suprapunând elemente noi peste cele anterioare. Pentru compoziții complexe se folosește tehnica desenării în mai multe straturi cu salvarea și restaurarea stării Canvas.
Paint — este clasa care definește cum va desena Canvas. Fără Paint, metodele Canvas sunt inutile — acesta setează culoarea, grosimea liniei, stilul de umplere, dimensiunea textului, efectele (umbră, gradient, estompare) și modul de amestecare.
Paint are proprietăți cheie: color (culoare), style (FILL, STROKE sau FILL_AND_STROKE), strokeWidth (grosimea liniei), textSize (dimensiunea textului), isAntiAlias (netezire), shader (gradient sau imagine). Crearea unui obiect Paint este o operație costisitoare, de aceea trebuie creat o dată și reutilizat.
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)
}
În acest exemplu se folosesc două Paint: unul pentru umplerea cercului cu violet, al doilea pentru conturul alb. Separarea în fill și stroke este o practică standard care permite gestionarea flexibilă a aspectului fără recrearea obiectelor.
Pentru umpleri cu gradient, Paint primește un Shader — subclasele LinearGradient, RadialGradient, SweepGradient. Shader se atașează la Paint prin metoda setShader() și definește cum se schimbă culoarea pe zona de desenare.
Canvas suportă transformări matriceale ale sistemului de coordonate: translate() (deplasare), rotate() (rotire), scale() (scalare) și skew() (înclinare). Transformările se acumulează: fiecare apel ulterior se aplică peste cele anterioare.
Gestionarea stivei de transformări se face prin save() și restore(). save() salvează starea curentă a Canvas (matrice, clip) în stivă, restore() restaurează ultima stare salvată. Între save() și restore() pot exista transformări imbricate.
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
canvas.save() // Salvează starea inițială
canvas.translate(100f, 100f) // Deplasează originea
// Primul pătrat
canvas.drawRect(0f, 0f, 50f, 50f, fillPaint)
canvas.save() // Salvează după deplasare
canvas.rotate(45f, 25f, 25f) // Rotește în jurul centrului pătratului
// Pătrat rotit
canvas.drawRect(0f, 0f, 50f, 50f, strokePaint)
canvas.restore() // Restaurează după rotire
canvas.restore() // Restaurează starea inițială
}
Folosiți întotdeauna save() înainte de a începe transformările și restore() după finalizarea lor. Save/restore dezechilibrate duc la acumularea matricei și desenarea incorectă a elementelor ulterioare.
Pentru grafică complexă, Canvas este combinat cu Bitmap și Path. Bitmap este folosit pentru încărcarea imaginilor și stocarea cadrelor randate. Path — pentru construirea de curbe Bezier arbitrare, poligoane și contururi complexe.
Tehnica de randare offscreen: se creează un Bitmap, pe el se desenează o scenă complexă printr-un Canvas separat, iar apoi Bitmap-ul rezultat este afișat în onDraw() prin drawBitmap(). Aceasta permite actualizarea graficii complexe fără redesenarea completă a fiecărui cadru.
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)
// Randează scena o dată
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 este folosit pentru construirea de contururi complexe: curbe Bezier (quadTo, cubicTo), arcuri (arcTo), linii (lineTo). Path poate fi închis (close()) pentru figuri umplute sau deschis pentru linii. Metodele principale drawPath() combină Path și Paint pentru desenarea finală.
Performanța desenării Canvas influențează direct FPS-ul aplicației. Problemele principale: alocări excesive în onDraw(), invalidate() frecvent și utilizarea neoptimală a Accelerării Hardware.
Paint, Path, Rect, RectF și alte obiecte trebuie create o singură dată în inițializator sau metoda setup(). Fiecare alocare în onDraw() creează sarcină pentru garbage collector, ceea ce duce la micro-întârzieri. Excepție — static sau companion object pentru obiecte imutabile.
Dacă o parte mare a View este ascunsă în afara zonei vizibile (de exemplu, în ScrollView), folosiți canvas.clipRect() pentru a limita desenarea. Accelerarea Hardware în acest caz optimizează suplimentar decuparea pixelilor invizibili.
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
// Decupează la zona vizibilă
canvas.save()
canvas.clipRect(scrollX, scrollY, scrollX + width, scrollY + height)
// Randează scena mare
renderScene(canvas)
canvas.restore()
}
Folosiți View.setLayerType() cu LAYER_TYPE_HARDWARE pentru View cu grafică Canvas complexă care se actualizează rar. Pentru animații actualizate frecvent, folosiți LAYER_TYPE_NONE pentru a evita actualizarea costisitoare a stratului hardware. Profilați prin GPU Profile Rendering și Android Studio Profiler.
Întrebări frecvente
Da, se poate crea Canvas(Bitmap) — un constructor care primește Bitmap. Aceasta se folosește pentru randarea offscreen: desenați pe Bitmap, apoi îl afișați prin drawBitmap(). Canvas poate fi creat și din Surface (pentru SurfaceView) sau prin Picture.
Canvas — este API pentru desenarea pe View în firul UI. SurfaceView — este un Surface separat care poate fi actualizat din firul de fundal prin propriul Canvas (lockCanvas() / unlockCanvasAndPost()). SurfaceView este preferat pentru jocuri și video.
Cea mai frecventă cauză sunt save() și restore() dezechilibrate. Dacă după transformare (translate, rotate) nu se apelează restore(), Canvas rămâne în sistemul de coordonate modificat, iar elementele următoare sunt desenate cu deplasare. Verificați perechile save()/restore().
Folosiți Paint.setTextAlign(Paint.Align.CENTER) pentru alinierea orizontală și Paint.getTextBounds() sau Paint.descent() / ascent() pentru verticală. Centrare: canvas.drawText(text, centerX, centerY - (ascent + descent) / 2, paint).
Canvas în sine nu animează — el doar desenează starea curentă. Animația se realizează prin apeluri succesive invalidate() (sau postInvalidateOnAnimation()), desenând de fiecare dată un nou cadru în onDraw(). Pentru animații line folosiți ValueAnimator sau Choreographer pentru sincronizarea cu Vsync.
Concluzii
Vom dezvolta o aplicație mobilă la cheie
IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.
Citiți și