onDraw() — ce este, mecanismul de apelare și suprascrierea metodei

Autor: IT Sectr Publicat: 2026-07-22 Timp de citire: 10 min

onDraw() — este metoda cheie a clasei View din Android SDK, responsabilă pentru redarea conținutului vizualizării. Sistemul apelează onDraw la fiecare actualizare a aspectului View — la redarea inițială, după invalidate() sau la modificarea dimensiunilor. Conform Android Developers Documentation (2026), suprascrierea onDraw utilizând Canvas API este principala modalitate de a crea grafică personalizată în aplicațiile Android.

Principalele puncte

  • onDraw(Canvas) — metodă View apelată de sistem pentru redarea conținutului pe Canvas
  • invalidate() — solicitare de redesare a View, care duce la apelarea onDraw în următorul ciclu de cadru
  • Canvas API oferă metodele drawCircle, drawBitmap, drawText, drawPath și altele pentru desenare
  • Sistemul Android execută redarea în firul UI, deci onDraw trebuie să fie ușor și rapid
  • Hardware acceleration accelerează automat redarea prin GPU, începând cu Android 3.0 (API 11)

Ce este onDraw()?

onDraw(Canvas canvas) — este o metodă protected a clasei android.view.View, care este apelată de sistemul Android atunci când View trebuie redat. Dezvoltatorul suprascrie această metodă pentru a implementa redarea personalizată: forme, text, imagini, gradiente, animații. Parametrul Canvas este suprafața grafică pe care se execută toate operațiile de desenare.

Semnătura metodei este simplă: override fun onDraw(canvas: Canvas). În interiorul acestei metode nu se pot crea obiecte noi (pentru a evita încărcarea GC), nu se pot executa operații lungi sau lucra cu rețeaua. View conține de asemenea metoda onDraw în ciclul său de viață între onLayout (calcularea pozițiilor) și dispatchDraw (redarea elementelor copil).

La moștenirea de la View, suprascrierea onDraw este obligatorie pentru afișarea oricărui conținut — altfel View va fi gol. La moștenirea de la subclase existente (TextView, ImageView, Button), suprascrierea onDraw este opțională și utilizată pentru adăugarea de grafică suplimentară peste conținutul standard.

Ciclul de redare al View

Android execută redarea View într-o succesiune strict definită, apelată din firul principal (UI thread). Ciclul complet constă din trei faze: measure (măsurarea dimensiunilor), layout (calcularea pozițiilor) și draw (redarea). onDraw face parte din faza draw și este apelat după ce dimensiunile și poziția View au fost deja determinate.

Faza draw: dispatchDraw și onDraw

draw(Canvas canvas) — este metoda publică a View care apelează onDraw și dispatchDraw. Metoda draw nu este destinată suprascrierii — în locul ei se suprascrie onDraw. După executarea onDraw pentru View-ul curent, este apelat dispatchDraw, care apelează recursiv draw pentru toate View-urile copil (dacă View-ul curent este un container ViewGroup).

Când apelează sistemul onDraw

onDraw este apelat în următoarele condiții: 1) View devine vizibil pentru prima dată (după setVisibility(VISIBLE)); 2) aplicația apelează invalidate() pentru acest View sau părintele său; 3) se modifică dimensiunea View (nou layout); 4) se modifică animația asociată View-ului (prin ValueAnimator sau ObjectAnimator); 5) se modifică starea View (pressed, focused, enabled).

Accelerarea hardware

Începând cu Android 3.0 (API 11), majoritatea operațiilor Canvas sunt executate cu accelerare hardware prin GPU. Dezvoltatorul poate verifica dacă accelerarea este activată prin canvas.isHardwareAccelerated(). La accelerarea hardware, unele operații Canvas nu sunt suportate (de exemplu, clipPath cu o zonă non-dreptunghiulară) și pot fi ignorate sau pot cauza erori.

Canvas API pentru grafică personalizată

Canvas oferă un set bogat de metode pentru desenare. Toate metodele funcționează în sistemul de coordonate curent, care poate fi modificat prin transformări (translate, rotate, scale, skew). Fiecare metodă primește un obiect Paint care definește culoarea, stilul, grosimea liniei, efectele și anti-aliasingul.

Metoda CanvasDestinațieParametrii cheie Paint
drawCircleDesenarea cerculuicenterX, centerY, radius, paint
drawRectDesenarea dreptunghiuluileft, top, right, bottom, paint
drawLineDesenarea linieistartX, startY, endX, endY, paint
drawPathDesenarea conturului arbitrarpath, paint
drawBitmapDesenarea imaginiibitmap, srcRect, dstRect, paint
drawTextDesenarea textuluitext, x, y, paint
drawArcDesenarea arcului sau sectoruluiovalRect, startAngle, sweepAngle, useCenter, paint
drawOvalDesenarea ovaluluiovalRect, paint

Obiectul Paint

Paint definește stilul de redare: culoarea (setColor), grosimea liniei (setStrokeWidth), stilul de umplere (setStyle — FILL, STROKE, FILL_AND_STROKE), anti-aliasingul (setAntiAlias), efectele (setShadowLayer, setMaskFilter) și gradientele (setShader). Se recomandă crearea și configurarea Paint o singură dată în constructorul View, nu în interiorul onDraw, pentru a evita crearea de obiecte în ciclul de redare.

Coordonate și transformări

Canvas utilizează un sistem de coordonate în care axa X este îndreptată spre dreapta, iar axa Y în jos. Originea coordonatelor (0, 0) este colțul stânga sus al View. Înainte de apelarea metodelor draw, dezvoltatorul poate modifica sistemul de coordonate prin canvas.translate(dx, dy), canvas.rotate(degrees, px, py), canvas.scale(sx, sy, px, py). Toate operațiile de desenare ulterioare vor fi executate în sistemul transformat.

Performanța onDraw

onDraw este executat în firul principal UI, iar durata sa influențează direct rata de cadre (fps). Pentru menținerea a 60 fps, metoda onDraw trebuie să se finalizeze în cel mult 16 milisecunde. Orice încetinire duce la omiterea cadrelor (jank), ceea ce este perceput vizual ca întreruperi ale animației sau întârziere la derulare.

Reguli pentru un onDraw performant

Regulile principale de optimizare onDraw includ: 1) nu creați obiecte în interiorul onDraw — toate Paint, Path, Rect trebuie să fie câmpuri ale clasei; 2) nu alocați memorie (new) — aceasta declanșează colectarea gunoiului (GC) și omiterea cadrelor; 3) cache-uiți operațiile complexe în Bitmap prin Bitmap.createBitmap și desenați Bitmap-ul pregătit; 4) nu executați operații de intrare-ieșire sau încărcare de resurse.

Utilizarea clipRect pentru optimizare

clipRect limitează zona de desenare la un dreptunghi specificat. Este utilizat pentru ca Canvas să nu piardă timp redând pixeli în afara zonei vizibile. Este deosebit de eficient la derularea listelor mari sau la scalare: setați clipRect pe zona vizibilă (dirty rect), iar Canvas va desena doar ceea ce este vizibil.

Layer — salvarea și restaurarea

canvas.save() salvează starea curentă a Canvas (transformarea și clip) în stivă. canvas.restore() restaurează ultima stare salvată. Acest lucru este necesar pentru transformări temporare: salvați starea, aplicați translate/rotate, desenați, apoi restaurați. save și restore sunt ușoare și nu afectează performanța la o utilizare rezonabilă (mai puțin de 10–15 niveluri de imbricare).

invalidate și postInvalidate

invalidate() — este metoda View care informează sistemul că conținutul View este învechit și trebuie redesenat. Apelarea invalidate inițiază un nou ciclu draw, în care va fi apelat onDraw. invalidate trebuie apelat doar din firul UI (main thread). Pentru apelarea din firul de fundal se utilizează postInvalidate.

postInvalidate() — versiunea thread-safe a invalidate, care trimite solicitarea de redesare în coada firului principal prin Handler. Este utilizată în sarcinile de fundal când trebuie actualizat View după finalizarea încărcării datelor sau a calculelor. Se combină cu runOnUiThread pentru sincronizarea cu UI.

Pentru redesarea parțială se utilizează invalidate(Rect dirty) sau invalidate(int l, int t, int r, int b). Aceasta indică sistemului că s-a modificat doar zona specificată, nu întregul View. Android optimizează redarea, redesenând doar dirty rect, ceea ce crește semnificativ performanța la animațiile cu actualizări parțiale.

Exemple de cod în Kotlin

Să analizăm exemple practice de suprascriere a onDraw pentru crearea de View-uri personalizate în Android cu Kotlin.

View personalizat — progres circular

Exemplul creează un indicator de progres circular cu umplere animată. onDraw desenează două cercuri: un fundal gri și unul colorat de umplere, al cărui arc este determinat de progresul curent. Apelarea invalidate în setProgress asigură redesarea la modificarea valorii.

kotlin
class CircularProgressView
    @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null
) : View(context, attrs) {

    private val bgPaint = Paint().apply {
        style = Paint.Style.STROKE
        strokeWidth = 12f
        color = Color.parseColor("#E0E0E0")
        isAntiAlias = true
    }

    private val progressPaint = Paint().apply {
        style = Paint.Style.STROKE
        strokeWidth = 12f
        color = Color.BLUE
        isAntiAlias = true
        strokeCap = Paint.Cap.ROUND
    }

    var progress: Float = 0f
        set(value) {
            field = value.coerceIn(0f, 1f)
            invalidate()
        }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        val size = minOf(width, height) - 24f
        val oval = RectF(12f, 12f,
                          12f + size, 12f + size)

        canvas.drawArc(oval, 270f, 360f, false, bgPaint)
        canvas.drawArc(oval, 270f,
                        progress * 360f,
                        false, progressPaint)
    }
}

View personalizat — fundal gradient cu text

Exemplul desenează un dreptunghi cu gradient și text deasupra acestuia. Gradientul este creat prin LinearGradient (Shader), iar textul prin drawText. Ambele Paint sunt create o singură dată în constructor, iar în onDraw se modifică doar dimensiunea dreptunghiului în funcție de lățimea curentă a View.

kotlin
class GradientTextView(context: Context)
    : View(context) {

    private val shaderPaint = Paint().apply {
        isAntiAlias = true
    }

    private val textPaint = Paint().apply {
        color = Color.WHITE
        textSize = 48f
        isAntiAlias = true
        textAlign = Paint.Align.CENTER
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)

        val gradient = LinearGradient(
            0f, 0f, width.toFloat(), 0f,
            Color.parseColor("#FF6B35"),
            Color.parseColor("#FF0040"),
            Shader.TileMode.CLAMP
        )
        shaderPaint.shader = gradient
        canvas.drawRect(0f, 0f,
                        width.toFloat(),
                        height.toFloat(), shaderPaint)

        val xCenter = width / 2f
        val yCenter = height / 2f
        val yOffset = (textPaint.descent() +
                        textPaint.ascent()) / 2f

        canvas.drawText("Salut de la Canvas",
                      xCenter, yCenter - yOffset,
                      textPaint)
    }
}

Ceas animat folosind invalidate

Exemplul creează un ceas animat care actualizează redarea la fiecare 1000 de milisecunde. postDelayed în onDraw apelează invalidate după o secundă, creând un ciclu infinit de redesare. Acele ceasului sunt desenate prin drawLine cu lungime și grosime diferite.

kotlin
class AnalogClockView(context: Context)
    : View(context) {

    private val handPaint = Paint().apply {
        style = Paint.Style.STROKE
        isAntiAlias = true
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        val cx = width / 2f
        val cy = height / 2f
        val radius = minOf(cx, cy) - 20f

        val now = Calendar.getInstance()
        val hours = now.get(Calendar.HOUR) % 12
        val minutes = now.get(Calendar.MINUTE)
        val seconds = now.get(Calendar.SECOND)

        val secondAngle = Math.toRadians(
            seconds * 6f - 90f)
        handPaint.strokeWidth = 2f
        handPaint.color = Color.RED
        canvas.drawLine(cx, cy,
            cx + cos(secondAngle) * radius * 0.8f,
            cy + sin(secondAngle) * radius * 0.8f,
            handPaint)

        postDelayed({ invalidate() }, 1000)
    }
}

Întrebări frecvente

Se poate apela onDraw direct?

Nu, nu trebuie să apelați onDraw direct. Pentru solicitarea redesării utilizați invalidate() (din firul UI) sau postInvalidate() (din firul de fundal). Sistemul decide singur când să apeleze onDraw și optimizează redarea, combinând multiple invalidate într-o singură trecere.

De ce onDraw nu este apelat după invalidate?

Cauze: View este ascuns (setVisibility(GONE) sau INVISIBLE); View nu a fost adăugat în ierarhia Window; dimensiunea View este 0 (width sau height = 0); se apelează setWillNotDraw(true) (implicit true pentru ViewGroup). Verificați fiecare dintre aceste puncte la depanare.

Cât de des poate fi apelat onDraw?

La 60 fps, onDraw poate fi apelat de până la 60 de ori pe secundă. La 120 fps (pe dispozitive cu afișaje de 120 Hz) — de până la 120 de ori. Fiecare apel trebuie să se încadreze în ~8–16 milisecunde. La animațiile active prin ObjectAnimator sau ValueAnimator, frecvența este determinată de numărul de cadre ale animației.

Care este diferența între onDraw și dispatchDraw?

onDraw răspunde de redarea conținutului propriu al View. dispatchDraw răspunde de redarea View-urilor copil (în ViewGroup). dispatchDraw este apelat după onDraw și traversează recursiv toate elementele copil. Pentru un View obișnuit (nu ViewGroup), dispatchDraw este gol.

Se poate utiliza Canvas în afara onDraw?

Da, Canvas poate fi creat pentru Bitmap prin Canvas(bitmap) și se poate desena pe el în orice fir. Acest lucru este utilizat pentru precalcularea graficii complexe în fundal. Totuși, Canvas transmis în onDraw aparține sistemului și poate fi utilizat doar în firul UI în interiorul metodei onDraw.

Rezumat

  • onDraw(Canvas) — metoda cheie View pentru redarea personalizată cu Canvas API în Android
  • Ciclul de redare constă din fazele measure, layout și draw — onDraw este apelat în faza draw
  • Canvas API include metodele drawCircle, drawRect, drawPath, drawBitmap, drawText și altele
  • invalidate() solicită redesarea din firul UI, postInvalidate() — din firul de fundal
  • Redesarea parțială prin invalidate(Rect) optimizează redarea, actualizând doar zona dirty
  • Performanța onDraw este critică: crearea de obiecte în interiorul metodei cauzează GC și omiterea cadrelor
  • Accelerarea hardware (de la API 11) accelerează automat operațiile Canvas prin GPU

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.

Discutați proiectul

Citiți și