onDraw() — co to je, mechanismus volání a přepisování metody

Autor: IT Sectr Publikováno: 2026-07-22 Doba čtení: 10 min

onDraw() — je klíčová metoda třídy View v Android SDK, zodpovědná za vykreslování obsahu pohledu. Systém volá onDraw při každé aktualizaci vzhledu View — při počátečním vykreslování, po invalidate() nebo při změně rozměrů. Podle Android Developers Documentation (2026) je přepisování onDraw pomocí Canvas API hlavním způsobem vytváření vlastní grafiky v Android aplikacích.

Hlavní body

  • onDraw(Canvas) — metoda View volaná systémem pro vykreslení obsahu na Canvas
  • invalidate() — požadavek na překreslení View, vedoucí k volání onDraw v dalším cyklu snímku
  • Canvas API poskytuje metody drawCircle, drawBitmap, drawText, drawPath a další pro kreslení
  • Systém Android provádí vykreslování v UI vlákně, proto musí být onDraw lehký a rychlý
  • Hardware acceleration automaticky zrychluje vykreslování přes GPU, počínaje Android 3.0 (API 11)

Co je onDraw()?

onDraw(Canvas canvas) — je protected metoda třídy android.view.View, která je volána systémem Android, když View musí být vykreslen. Vývojář tuto metodu přepisuje, aby implementoval vlastní vykreslování: tvary, text, obrázky, přechody, animace. Parametr Canvas je grafický povrch, na kterém jsou prováděny všechny operace kreslení.

Signatura metody je jednoduchá: override fun onDraw(canvas: Canvas). Uvnitř této metody nelze vytvářet nové objekty (aby se zabránilo zatížení GC), provádět dlouhé operace nebo pracovat se sítí. View také obsahuje metodu onDraw ve svém životním cyklu mezi onLayout (výpočet pozic) a dispatchDraw (vykreslování podřízených prvků).

Při dědění z View je přepsání onDraw povinné pro zobrazení jakéhokoli obsahu — jinak bude View prázdné. Při dědění z existujících podtříd (TextView, ImageView, Button) je přepsání onDraw volitelné a používá se pro přidání další grafiky nad standardní obsah.

Cyklus vykreslování View

Android provádí vykreslování View v přesně definovaném pořadí, volaném z hlavního vlákna (UI thread). Celý cyklus se skládá ze tří fází: measure (měření rozměrů), layout (výpočet pozic) a draw (vykreslení). onDraw je součástí fáze draw a je volán poté, co jsou rozměry a pozice View již určeny.

Fáze draw: dispatchDraw a onDraw

draw(Canvas canvas) — je veřejná metoda View, která volá onDraw a dispatchDraw. Metoda draw není určena k přepsání — místo ní se přepisuje onDraw. Po provedení onDraw pro aktuální View je volán dispatchDraw, který rekurzivně volá draw pro všechny podřízené View (pokud je aktuální View kontejner ViewGroup).

Kdy systém volá onDraw

onDraw je volán za následujících podmínek: 1) View se poprvé stane viditelným (po setVisibility(VISIBLE)); 2) aplikace volá invalidate() pro tento View nebo jeho rodiče; 3) změní se velikost View (nový layout); 4) změní se animace spojená s View (přes ValueAnimator nebo ObjectAnimator); 5) změní se stav View (pressed, focused, enabled).

Hardwarová akcelerace

Od Android 3.0 (API 11) je většina operací Canvas prováděna s hardwarovou akcelerací přes GPU. Vývojář může zkontrolovat, zda je akcelerace zapnuta, pomocí canvas.isHardwareAccelerated(). Při hardwarové akceleraci některé operace Canvas nejsou podporovány (např. clipPath s neobdélníkovou oblastí) a mohou být ignorovány nebo způsobovat chyby.

Canvas API pro vlastní grafiku

Canvas poskytuje bohatou sadu metod pro kreslení. Všechny metody pracují v aktuálním souřadnicovém systému, který lze změnit pomocí transformací (translate, rotate, scale, skew). Každá metoda přijímá objekt Paint, který definuje barvu, styl, tloušťku čáry, efekty a vyhlazování.

Metoda CanvasÚčelKlíčové parametry Paint
drawCircleKreslení kruhucenterX, centerY, radius, paint
drawRectKreslení obdélníkuleft, top, right, bottom, paint
drawLineKreslení čárystartX, startY, endX, endY, paint
drawPathKreslení libovolného obrysupath, paint
drawBitmapKreslení obrázkubitmap, srcRect, dstRect, paint
drawTextKreslení textutext, x, y, paint
drawArcKreslení oblouku nebo sektoruovalRect, startAngle, sweepAngle, useCenter, paint
drawOvalKreslení ováluovalRect, paint

Objekt Paint

Paint definuje styl vykreslování: barvu (setColor), tloušťku čáry (setStrokeWidth), styl výplně (setStyle — FILL, STROKE, FILL_AND_STROKE), vyhlazování (setAntiAlias), efekty (setShadowLayer, setMaskFilter) a přechody (setShader). Doporučuje se vytvořit a nakonfigurovat Paint jednou v konstruktoru View, ne uvnitř onDraw, aby se zabránilo vytváření objektů v cyklu vykreslování.

Souřadnice a transformace

Canvas používá souřadnicový systém, kde osa X směřuje doprava a osa Y dolů. Počátek souřadnic (0, 0) je levý horní roh View. Před voláním metod draw může vývojář změnit souřadnicový systém pomocí canvas.translate(dx, dy), canvas.rotate(degrees, px, py), canvas.scale(sx, sy, px, py). Všechny následné operace kreslení budou prováděny v transformovaném systému.

Výkon onDraw

onDraw je prováděn v hlavním UI vlákně a jeho doba trvání přímo ovlivňuje snímkovou frekvenci (fps). Pro udržení 60 fps musí metoda onDraw skončit nejpozději do 16 milisekund. Jakékoli zpomalení vede k přeskakování snímků (jank), což je vizuálně vnímáno jako škubání animace nebo zpoždění při rolování.

Pravidla pro výkonný onDraw

Základní pravidla optimalizace onDraw zahrnují: 1) nevytvářet objekty uvnitř onDraw — všechny Paint, Path, Rect by měly být poli třídy; 2) nealokovat paměť (new) — to spouští garbage collection (GC) a přeskakování snímků; 3) ukládat složité operace do mezipaměti v Bitmap pomocí Bitmap.createBitmap a kreslit připravený Bitmap; 4) neprovádět operace vstupu-výstupu nebo načítání zdrojů.

Použití clipRect pro optimalizaci

clipRect omezuje oblast kreslení na zadaný obdélník. Používá se, aby Canvas neztrácel čas vykreslováním pixelů mimo viditelnou oblast. Zvláště účinný při rolování velkých seznamů nebo změně měřítka: nastavte clipRect na viditelnou oblast (dirty rect) a Canvas bude kreslit pouze to, co je vidět.

Layer — ukládání a obnovování

canvas.save() ukládá aktuální stav Canvas (transformaci a clip) do zásobníku. canvas.restore() obnovuje poslední uložený stav. To je nezbytné pro dočasné transformace: uložíte stav, aplikujete translate/rotate, kreslíte, poté obnovíte. save a restore jsou lehké a při rozumném použití (méně než 10–15 úrovní vnoření) neovlivňují výkon.

invalidate a postInvalidate

invalidate() — je metoda View, která informuje systém, že obsah View je zastaralý a je třeba jej překreslit. Volání invalidate iniciuje nový cyklus draw, ve kterém bude volán onDraw. invalidate by měl být volán pouze z UI vlákna (main thread). Pro volání z vlákna na pozadí se používá postInvalidate.

postInvalidate() — thread-safe verze invalidate, která odesílá požadavek na překreslení do fronty hlavního vlákna přes Handler. Používá se v úlohách na pozadí, když je třeba aktualizovat View po dokončení načítání dat nebo výpočtů. Kombinuje se s runOnUiThread pro synchronizaci s UI.

Pro částečné překreslení se používá invalidate(Rect dirty) nebo invalidate(int l, int t, int r, int b). Toto systému indikuje, že se změnila pouze určená oblast, nikoli celý View. Android optimalizuje vykreslování, překresluje pouze dirty rect, což výrazně zvyšuje výkon u animací s částečnými aktualizacemi.

Příklady kódu v Kotlinu

Podívejme se na praktické příklady přepisování onDraw pro vytváření vlastních View v Androidu pomocí Kotlinu.

Vlastní View — kruhový průběh

Příklad vytváří kruhový indikátor průběhu s animovaným plněním. onDraw kreslí dva kruhy: šedé pozadí a barevnou výplň, jejíž oblouk je určen aktuálním průběhem. Volání invalidate v setProgress zajišťuje překreslení při změně hodnoty.

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

Vlastní View — přechodové pozadí s textem

Příklad kreslí obdélník s přechodem a textem na něm. Přechod je vytvořen pomocí LinearGradient (Shader) a text pomocí drawText. Oba Paint jsou vytvořeny jednou v konstruktoru a v onDraw se mění pouze velikost obdélníku v závislosti na aktuální šířce 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("Ahoj z Canvas",
                      xCenter, yCenter - yOffset,
                      textPaint)
    }
}

Animované hodiny pomocí invalidate

Příklad vytváří animované hodiny, které aktualizují vykreslování každých 1000 milisekund. postDelayed v onDraw volá invalidate po jedné sekundě, čímž vytváří nekonečný cyklus překreslování. Ručičky hodin jsou kresleny pomocí drawLine s různou délkou a tloušťkou.

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

Často kladené otázky

Lze onDraw volat přímo?

Ne, onDraw by se neměl volat přímo. Pro požadavek na překreslení použijte invalidate() (z UI vlákna) nebo postInvalidate() (z vlákna na pozadí). Systém sám rozhodne, kdy zavolat onDraw, a optimalizuje vykreslování sloučením více volání invalidate do jednoho průchodu.

Proč není onDraw volán po invalidate?

Důvody: View je skrytý (setVisibility(GONE) nebo INVISIBLE); View nebyl přidán do hierarchie Window; velikost View je 0 (width nebo height = 0); volá se setWillNotDraw(true) (výchozí true pro ViewGroup). Při ladění zkontrolujte každý z těchto bodů.

Jak často může být onDraw volán?

Při 60 fps může být onDraw volán až 60krát za sekundu. Při 120 fps (na zařízeních s 120 Hz displeji) — až 120krát. Každé volání by se mělo vejít do ~8–16 milisekund. Při aktivních animacích přes ObjectAnimator nebo ValueAnimator je frekvence určena počtem snímků animace.

Jaký je rozdíl mezi onDraw a dispatchDraw?

onDraw je zodpovědný za vykreslení obsahu samotného View. dispatchDraw je zodpovědný za vykreslení podřízených View (v ViewGroup). dispatchDraw je volán po onDraw a rekurzivně prochází všechny podřízené prvky. Pro běžný View (ne ViewGroup) je dispatchDraw prázdný.

Lze Canvas použít mimo onDraw?

Ano, Canvas lze vytvořit pro Bitmap pomocí Canvas(bitmap) a kreslit na něj v libovolném vlákně. To se používá pro předvýpočet složité grafiky na pozadí. Nicméně Canvas předávaný v onDraw patří systému a může být použit pouze v UI vlákně uvnitř metody onDraw.

Shrnutí

  • onDraw(Canvas) — klíčová metoda View pro vlastní vykreslování pomocí Canvas API v Androidu
  • Cyklus vykreslování se skládá z fází measure, layout a draw — onDraw je volán ve fázi draw
  • Canvas API zahrnuje metody drawCircle, drawRect, drawPath, drawBitmap, drawText a další
  • invalidate() žádá o překreslení z UI vlákna, postInvalidate() — z vlákna na pozadí
  • Částečné překreslení pomocí invalidate(Rect) optimalizuje vykreslování, aktualizuje pouze dirty oblast
  • Výkon onDraw je kritický: vytváření objektů uvnitř metody způsobuje GC a přeskakování snímků
  • Hardwarová akcelerace (od API 11) automaticky zrychluje operace Canvas přes GPU

Vyvineme mobilní aplikaci na klíč

IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.

Prodiskutovat projekt

Přečtěte si také