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 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.
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.
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).
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).
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 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 | Účel | Klíčové parametry Paint |
|---|---|---|
| drawCircle | Kreslení kruhu | centerX, centerY, radius, paint |
| drawRect | Kreslení obdélníku | left, top, right, bottom, paint |
| drawLine | Kreslení čáry | startX, startY, endX, endY, paint |
| drawPath | Kreslení libovolného obrysu | path, paint |
| drawBitmap | Kreslení obrázku | bitmap, srcRect, dstRect, paint |
| drawText | Kreslení textu | text, x, y, paint |
| drawArc | Kreslení oblouku nebo sektoru | ovalRect, startAngle, sweepAngle, useCenter, paint |
| drawOval | Kreslení oválu | ovalRect, 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í.
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.
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í.
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ů.
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.
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() — 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.
Podívejme se na praktické příklady přepisování onDraw pro vytváření vlastních View v Androidu pomocí Kotlinu.
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.
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)
}
}
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.
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)
}
}
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.
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
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.
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ů.
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.
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ý.
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í
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í.
Přečtěte si také