onDraw() — mi ez, a meghívás mechanizmusa és a metódus felülírása

Szerző: IT Sectr Megjelenés: 2026-07-22 Olvasási idő: 10 perc

onDraw() — a View osztály kulcsfontosságú metódusa az Android SDK-ban, amely a nézet tartalmának megjelenítéséért felelős. A rendszer az onDraw-ot hívja meg a View megjelenésének minden frissítésekor — a kezdeti rendereléskor, invalidate() után vagy a méretek megváltozásakor. Az Android Developers Documentation (2026) szerint az onDraw felülírása a Canvas API használatával az elsődleges módja az egyedi grafika létrehozásának Android alkalmazásokban.

Főbb pontok

  • onDraw(Canvas) — View metódus, amelyet a rendszer hív meg a tartalom Canvas-ra rendereléséhez
  • invalidate() — kérés a View újrarajzolására, ami az onDraw meghívásához vezet a következő frame ciklusban
  • Canvas API drawCircle, drawBitmap, drawText, drawPath és egyéb rajzolási metódusokat biztosít
  • Az Android rendszer a UI szálon végzi a renderelést, ezért az onDraw-nak könnyűnek és gyorsnak kell lennie
  • Hardware acceleration automatikusan gyorsítja a renderelést GPU-n keresztül, Android 3.0-tól (API 11)

Mi az onDraw()?

onDraw(Canvas canvas) — az android.view.View osztály protected metódusa, amelyet az Android rendszer hív meg, amikor a View-t renderelni kell. A fejlesztő ezt a metódust írja felül az egyedi renderelés megvalósításához: alakzatok, szöveg, képek, gradiensek, animáció. A Canvas paraméter az a grafikus felület, amelyen az összes rajzolási művelet végrehajtódik.

A metódus szignatúrája egyszerű: override fun onDraw(canvas: Canvas). Ezen metóduson belül nem lehet új objektumokat létrehozni (a GC terhelés elkerülése érdekében), hosszú műveleteket végrehajtani vagy hálózattal dolgozni. A View szintén tartalmazza az onDraw metódust az életciklusában az onLayout (pozíciók kiszámítása) és a dispatchDraw (gyermek elemek renderelése) között.

A View-ból való származtatáskor az onDraw felülírása kötelező bármilyen tartalom megjelenítéséhez — ellenkező esetben a View üres lesz. Meglévő alosztályokból (TextView, ImageView, Button) való származtatáskor az onDraw felülírása opcionális, és a standard tartalom fölé további grafika hozzáadására szolgál.

View renderelési ciklusa

Android a View renderelését szigorúan meghatározott sorrendben végzi, amelyet a fő szálból (UI thread) hív meg. A teljes ciklus három fázisból áll: measure (méretek mérése), layout (pozíciók kiszámítása) és draw (renderelés). Az onDraw a draw fázis része, és a View méreteinek és pozíciójának meghatározása után hívódik meg.

Draw fázis: dispatchDraw és onDraw

draw(Canvas canvas) — a View publikus metódusa, amely meghívja az onDraw-ot és a dispatchDraw-ot. A draw metódus nem felülírásra szolgál — helyette az onDraw-t kell felülírni. Az aktuális View onDraw-jának végrehajtása után a dispatchDraw hívódik meg, amely rekurzívan meghívja a draw-ot az összes gyermek View-ra (ha az aktuális View ViewGroup konténer).

Mikor hívja a rendszer az onDraw-ot

onDraw a következő feltételek mellett hívódik meg: 1) a View először válik láthatóvá (setVisibility(VISIBLE) után); 2) az alkalmazás invalidate()-ot hív erre a View-ra vagy szülőjére; 3) a View mérete megváltozik (új layout); 4) a View-hoz kapcsolódó animáció megváltozik (ValueAnimator vagy ObjectAnimator révén); 5) a View állapota megváltozik (pressed, focused, enabled).

Hardveres gyorsítás

Android 3.0 (API 11) óta a legtöbb Canvas művelet hardveres gyorsítással, GPU-n keresztül történik. A fejlesztő ellenőrizheti, hogy a gyorsítás be van-e kapcsolva a canvas.isHardwareAccelerated() segítségével. Hardveres gyorsítás esetén egyes Canvas műveletek nem támogatottak (pl. clipPath nem négyszögletes területtel) és figyelmen kívül hagyhatók vagy hibát okozhatnak.

Canvas API egyedi grafikához

Canvas gazdag metóduskészletet biztosít a rajzoláshoz. Minden metódus az aktuális koordináta-rendszerben működik, amely transzformációkkal (translate, rotate, scale, skew) módosítható. Minden metódus egy Paint objektumot fogad, amely meghatározza a színt, stílust, vonalvastagságot, effekteket és él simítást.

Canvas metódusCélPaint kulcsparaméterei
drawCircleKör rajzolásacenterX, centerY, radius, paint
drawRectTéglalap rajzolásaleft, top, right, bottom, paint
drawLineVonal rajzolásastartX, startY, endX, endY, paint
drawPathTetszőleges kontúr rajzolásapath, paint
drawBitmapKép rajzolásabitmap, srcRect, dstRect, paint
drawTextSzöveg rajzolásatext, x, y, paint
drawArcÍv vagy szektor rajzolásaovalRect, startAngle, sweepAngle, useCenter, paint
drawOvalOvális rajzolásaovalRect, paint

Paint objektum

Paint meghatározza a renderelési stílust: szín (setColor), vonalvastagság (setStrokeWidth), kitöltési stílus (setStyle — FILL, STROKE, FILL_AND_STROKE), él simítás (setAntiAlias), effektek (setShadowLayer, setMaskFilter) és gradiensek (setShader). Javasolt a Paint-et egyszer létrehozni és konfigurálni a View konstruktorában, nem az onDraw-on belül, hogy elkerüljük az objektumok létrehozását a renderelési ciklusban.

Koordináták és transzformációk

Canvas olyan koordináta-rendszert használ, ahol az X tengely jobbra, az Y tengely pedig lefelé mutat. A koordináta-rendszer kezdőpontja (0, 0) a View bal felső sarka. A draw metódusok meghívása előtt a fejlesztő módosíthatja a koordináta-rendszert a canvas.translate(dx, dy), canvas.rotate(degrees, px, py), canvas.scale(sx, sy, px, py) segítségével. Minden további rajzolási művelet a transzformált rendszerben fog végrehajtódni.

Az onDraw teljesítménye

onDraw a fő UI szálon fut, és időtartama közvetlenül befolyásolja a képkocka sebességet (fps). A 60 fps fenntartásához az onDraw metódusnak legfeljebb 16 ezredmásodpercen belül be kell fejeződnie. Bármilyen lassulás képkocka kihagyáshoz (jank) vezet, ami vizuálisan az animáció akadozásaként vagy görgetés közbeni késésként érzékelhető.

A hatékony onDraw szabályai

Az alapvető szabályok az onDraw optimalizálásához a következők: 1) ne hozzon létre objektumokat az onDraw-on belül — minden Paint, Path, Rect osztály mező legyen; 2) ne foglaljon memóriát (new) — ez szemétgyűjtést (GC) és képkocka kihagyást vált ki; 3) gyorsítótárazza az összetett műveleteket Bitmap-ben a Bitmap.createBitmap segítségével, és rajzolja ki a kész Bitmap-et; 4) ne végezzen be-ki viteli műveleteket vagy erőforrás betöltést.

clipRect használata optimalizáláshoz

clipRect a rajzolási területet egy meghatározott téglalapra korlátozza. Arra szolgál, hogy a Canvas ne vesztegessen időt a látható területen kívüli pixelek renderelésére. Különösen hatékony nagy listák görgetésekor vagy méretezéskor: állítsa a clipRect-et a látható területre (dirty rect), és a Canvas csak azt rajzolja ki, ami látható.

Layer — mentés és visszaállítás

canvas.save() elmenti a Canvas aktuális állapotát (transzformáció és clip) a verembe. canvas.restore() visszaállítja az utolsó mentett állapotot. Ez szükséges az ideiglenes transzformációkhoz: menti az állapotot, alkalmazza a translate/rotate-ot, rajzol, majd visszaállít. A save és restore könnyű, és ésszerű használat mellett (kevesebb mint 10–15 beágyazási szint) nem befolyásolja a teljesítményt.

invalidate és postInvalidate

invalidate() — a View metódusa, amely értesíti a rendszert, hogy a View tartalma elavult, és újra kell rajzolni. Az invalidate hívás egy új draw ciklust indít, amelyben az onDraw meghívásra kerül. Az invalidate-ot csak a UI szálból (main thread) szabad meghívni. Háttérszálból történő híváshoz a postInvalidate használatos.

postInvalidate() — az invalidate szálbiztos változata, amely az újrarajzolási kérést a Handleren keresztül a fő szál sorába küldi. Háttérfeladatokban használatos, amikor a View-t frissíteni kell az adatbetöltés vagy számítások befejezése után. A UI-val való szinkronizáláshoz runOnUiThread-tel kombinálják.

Részleges újrarajzoláshoz az invalidate(Rect dirty) vagy invalidate(int l, int t, int r, int b) használatos. Ez jelzi a rendszernek, hogy csak a megadott terület változott, nem a teljes View. Az Android optimalizálja a renderelést, csak a dirty rect-et rajzolja újra, ami jelentősen növeli a teljesítményt a részleges frissítésekkel rendelkező animációknál.

Kódpéldák Kotlinban

Nézzük meg gyakorlati példákat az onDraw felülírására egyedi View-k létrehozásához Android-ban Kotlin segítségével.

Egyedi View — kör alakú folyamatjelző

A példa egy kör alakú folyamatjelzőt hoz létre animált kitöltéssel. Az onDraw két kört rajzol: egy szürke hátteret és egy színes kitöltést, amelynek ívét az aktuális folyamat határozza meg. Az invalidate hívás a setProgress-ben biztosítja az újrarajzolást az érték változásakor.

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

Egyedi View — gradiens háttér szöveggel

A példa egy gradienssel ellátott téglalapot rajzol szöveggel a tetején. A gradiens LinearGradient (Shader) segítségével, a szöveg pedig drawText segítségével jön létre. Mindkét Paint egyszer jön létre a konstruktorban, és az onDraw-ban csak a téglalap mérete változik a View aktuális szélességétől függően.

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("Helló a Canvas-tól",
                      xCenter, yCenter - yOffset,
                      textPaint)
    }
}

Animált óra invalidate használatával

A példa egy animált órát hoz létre, amely 1000 ezredmásodpercenként frissíti a renderelést. A postDelayed az onDraw-ban egy másodperc után meghívja az invalidate-ot, létrehozva egy végtelen újrarajzolási ciklust. Az óra mutatói a drawLine segítségével rajzolódnak ki különböző hosszúsággal és vastagsággal.

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

Gyakran ismételt kérdések

Meghívható az onDraw közvetlenül?

Nem, az onDraw-ot nem szabad közvetlenül meghívni. Újrarajzolás kéréséhez használja az invalidate()-ot (UI szálból) vagy a postInvalidate()-ot (háttérszálból). A rendszer maga dönti el, mikor hívja meg az onDraw-ot, és optimalizálja a renderelést, több invalidate hívást egy menetbe egyesítve.

Miért nem hívódik meg az onDraw invalidate után?

Okok: a View el van rejtve (setVisibility(GONE) vagy INVISIBLE); a View nincs hozzáadva a Window hierarchiához; a View mérete 0 (width vagy height = 0); setWillNotDraw(true) hívódik (alapértelmezés szerint true ViewGroup esetén). Hibakereséskor ellenőrizze ezeket a pontokat.

Milyen gyakran hívódhat meg az onDraw?

60 fps esetén az onDraw akár 60-szor is meghívódhat másodpercenként. 120 fps esetén (120 Hz-es kijelzővel rendelkező eszközökön) — akár 120-szor. Minden hívásnak ~8–16 ezredmásodpercen belül be kell fejeződnie. Aktív ObjectAnimator vagy ValueAnimator animációk esetén a gyakoriságot az animációs képkockák száma határozza meg.

Mi a különbség az onDraw és a dispatchDraw között?

onDraw a View saját tartalmának rendereléséért felelős. dispatchDraw a gyermek View-k rendereléséért felelős (ViewGroup-ban). A dispatchDraw az onDraw után hívódik, és rekurzívan bejárja az összes gyermek elemet. Egy szokványos View (nem ViewGroup) esetén a dispatchDraw üres.

Használható a Canvas az onDraw-on kívül?

Igen, a Canvas létrehozható Bitmap számára a Canvas(bitmap) segítségével, és bármely szálon lehet rá rajzolni. Ez az összetett grafika háttérben történő előzetes kiszámítására használatos. Az onDraw-ban átadott Canvas azonban a rendszeré, és csak a UI szálon belül, az onDraw metóduson belül használható.

Összefoglalás

  • onDraw(Canvas) — kulcsfontosságú View metódus egyedi rendereléshez Canvas API használatával Androidban
  • Renderelési ciklus measure, layout és draw fázisokból áll — onDraw a draw fázisban hívódik
  • Canvas API tartalmazza a drawCircle, drawRect, drawPath, drawBitmap, drawText és egyéb metódusokat
  • invalidate() újrarajzolást kér a UI szálból, postInvalidate() — a háttérszálból
  • Részleges újrarajzolás invalidate(Rect) segítségével optimalizálja a renderelést, csak a dirty területet frissítve
  • Az onDraw teljesítménye kritikus: objektumok létrehozása a metóduson belül GC-t és képkocka kihagyást okoz
  • Hardveres gyorsítás (API 11-től) automatikusan gyorsítja a Canvas műveleteket GPU-n keresztül

Kulcsrakész mobilalkalmazást fejlesztünk

Az IT Sectr 2017 óta készít iOS és Android alkalmazásokat induló vállalkozásoknak és vállalkozásoknak. Tanácsot adunk, és a legjobb megoldást javasoljuk.

Projekt megbeszélése

Olvassa el is