Canvas v Androidu — je třída z balíčku android.graphics poskytující API pro 2D kreslení na View. Canvas funguje jako plátno, na kterém vývojář kreslí geometrické tvary, text, obrázky a cesty pomocí různých metod draw*. Podle Android Developers Documentation (2025) je Canvas hlavním nástrojem pro vlastní kreslení v Androidu a používá se společně s třídou Paint pro nastavení stylů a barev. Každé volání onDraw() obdrží připravenou instanci Canvas spojenou s bitmapovým bufferem view.
Hlavní body
Canvas — je centrální třída 2D grafiky v Androidu, která poskytuje metody pro kreslení na plochu. Každý Canvas je připojen k Bitmap — paměťové oblasti, do které se zapisují výsledky kreslicích operací. Když je View překresleno, systém předá do onDraw() Canvas již spojený s bufferem tohoto View.
Canvas pracuje v souřadnicovém systému, kde (0,0) je levý horní roh, osa X směřuje doprava, osa Y dolů. Všechny kreslicí operace používají aktuální stav Canvas včetně transformační matice, ořezové oblasti (clip) a stylu Paint. Podle Android Documentation (2025) zahrnuje Canvas API více než 30 metod draw* pro různé typy grafických primitiv.
Od Androidu 3.0 (API 11) Canvas podporuje hardwarovou akceleraci prostřednictvím DisplayList. V akcelerovaném režimu se operace Canvas zapisují do DisplayList a poté se provádějí na GPU. To přináší výrazné zvýšení výkonu, ale ukládá omezení: některé operace Canvas (clipPath s ne-Path ořezem) nejsou v akcelerovaném režimu podporovány.
Canvas poskytuje sadu metod pro kreslení základních primitiv. Každá metoda přijímá geometrické parametry a objekt Paint, který určuje vizuální styl.
| Metoda | Účel | Příklad |
|---|---|---|
| drawRect() | Kreslení obdélníku | canvas.drawRect(10f, 10f, 100f, 100f, paint) |
| drawCircle() | Kreslení kruhu | canvas.drawCircle(50f, 50f, 30f, paint) |
| drawLine() | Kreslení čáry | canvas.drawLine(0f, 0f, 100f, 100f, paint) |
| drawText() | Kreslení textu | canvas.drawText("Hello", 10f, 20f, paint) |
| drawBitmap() | Kreslení obrázku | canvas.drawBitmap(bitmap, 0f, 0f, paint) |
| drawPath() | Kreslení libovolné cesty | canvas.drawPath(path, paint) |
| drawOval() | Kreslení oválu | canvas.drawOval(rectF, paint) |
| drawArc() | Kreslení oblouku nebo sektoru | canvas.drawArc(rectF, 0f, 90f, true, paint) |
Každá metoda může být volána v libovolném pořadí — Canvas kreslí v pořadí volání a nové prvky umísťuje přes předchozí. Pro složité kompozice se používá technika vícevrstvého kreslení s ukládáním a obnovováním stavu Canvas.
Paint — je třída, která určuje, jak bude Canvas kreslit. Bez Paint jsou metody Canvas k ničemu — nastavuje barvu, tloušťku čáry, styl výplně, velikost textu, efekty (stín, přechod, rozostření) a režim prolnutí.
Paint má klíčové vlastnosti: color (barva), style (FILL, STROKE nebo FILL_AND_STROKE), strokeWidth (tloušťka čáry), textSize (velikost textu), isAntiAlias (vyhlazování), shader (přechod nebo obrázek). Vytvoření objektu Paint je nákladná operace, proto by se měl vytvořit jednou a znovu používat.
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)
}
V tomto příkladu se používají dva Paint: jeden pro vyplnění kruhu fialovou barvou, druhý pro bílý obrys. Rozdělení na fill a stroke je standardní praxe, která umožňuje flexibilně spravovat vzhled bez přetváření objektů.
Pro přechodové výplně Paint přijímá Shader — podtřídy LinearGradient, RadialGradient, SweepGradient. Shader se připojuje k Paint prostřednictvím metody setShader() a určuje, jak se barva mění v oblasti kreslení.
Canvas podporuje maticové transformace souřadnicového systému: translate() (posun), rotate() (otočení), scale() (škálování) a skew() (zkosení). Transformace se sčítají: každé následující volání se aplikuje na předchozí.
Správa zásobníku transformací se provádí pomocí save() a restore(). save() ukládá aktuální stav Canvas (matici, clip) do zásobníku, restore() obnovuje poslední uložený stav. Mezi save() a restore() mohou být vnořené transformace.
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
canvas.save() // Uložit počáteční stav
canvas.translate(100f, 100f) // Posunout počátek
// První čtverec
canvas.drawRect(0f, 0f, 50f, 50f, fillPaint)
canvas.save() // Uložit po posunu
canvas.rotate(45f, 25f, 25f) // Otočit kolem středu čtverce
// Otočený čtverec
canvas.drawRect(0f, 0f, 50f, 50f, strokePaint)
canvas.restore() // Obnovit po otočení
canvas.restore() // Obnovit počáteční stav
}
Vždy používejte save() před zahájením transformací a restore() po jejich dokončení. Nevyvážené save/restore vedou k hromadění matice a nesprávnému kreslení následujících prvků.
Pro složitou grafiku se Canvas kombinuje s Bitmap a Path. Bitmap se používá pro načítání obrázků a ukládání vykreslených snímků do mezipaměti. Path — pro vytváření libovolných Bézierových křivek, mnohoúhelníků a složitých obrysů.
Technika offscreen vykreslování: vytvoří se Bitmap, na něj se vykreslí složitá scéna přes samostatný Canvas, a poté se hotový Bitmap zobrazí v onDraw() pomocí drawBitmap(). To umožňuje aktualizovat složitou grafiku bez úplného překreslování každého snímku.
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)
// Vykreslit scénu jednou
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 se používá pro vytváření složitých obrysů: Bézierovy křivky (quadTo, cubicTo), oblouky (arcTo), čáry (lineTo). Path může být uzavřený (close()) pro vyplněné tvary nebo otevřený pro čáry. Hlavní metody drawPath() kombinují Path a Paint pro konečné kreslení.
Výkon kreslení Canvas přímo ovlivňuje FPS aplikace. Hlavní problémy: nadměrné alokace v onDraw(), časté invalidate() a neoptimální využití hardwarové akcelerace.
Paint, Path, Rect, RectF a další objekty by měly být vytvořeny jednou v inicializátoru nebo metodě setup(). Každá alokace v onDraw() zatěžuje garbage collector, což vede k mikro-zasekávání. Výjimka — static nebo companion object pro neměnné objekty.
Pokud je velká část View skryta mimo viditelnou oblast (např. v ScrollView), použijte canvas.clipRect() pro omezení kreslení. Hardwarová akcelerace v tomto případě dále optimalizuje ořezávání neviditelných pixelů.
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
// Oříznout na viditelnou oblast
canvas.save()
canvas.clipRect(scrollX, scrollY, scrollX + width, scrollY + height)
// Vykreslit velkou scénu
renderScene(canvas)
canvas.restore()
}
Používejte View.setLayerType() s LAYER_TYPE_HARDWARE pro View se složitou Canvas grafikou, která se zřídka aktualizuje. Pro často aktualizované animace používejte LAYER_TYPE_NONE, abyste se vyhnuli nákladné aktualizaci hardwarové vrstvy. Profilujte pomocí GPU Profile Rendering a Android Studio Profiler.
Často kladené otázky
Ano, lze vytvořit Canvas(Bitmap) — konstruktor přijímající Bitmap. To se používá pro offscreen vykreslování: kreslíte na Bitmap a poté jej zobrazíte pomocí drawBitmap(). Canvas lze také vytvořit z Surface (pro SurfaceView) nebo přes Picture.
Canvas — je API pro kreslení na View v UI vlákně. SurfaceView — je samostatný Surface, který lze aktualizovat z vlákna na pozadí prostřednictvím vlastního Canvas (lockCanvas() / unlockCanvasAndPost()). SurfaceView je vhodnější pro hry a video.
Nejčastější příčinou jsou nevyvážené save() a restore(). Pokud po transformaci (translate, rotate) nezavoláte restore(), Canvas zůstává v změněném souřadnicovém systému a následující prvky se kreslí s posunem. Zkontrolujte páry save()/restore().
Použijte Paint.setTextAlign(Paint.Align.CENTER) pro horizontální zarovnání a Paint.getTextBounds() nebo Paint.descent() / ascent() pro vertikální. Zarovnání: canvas.drawText(text, centerX, centerY - (ascent + descent) / 2, paint).
Canvas sám o sobě neanimuje — pouze kreslí aktuální stav. Animace se dosahuje postupným voláním invalidate() (nebo postInvalidateOnAnimation()), přičemž se pokaždé v onDraw() kreslí nový snímek. Plynulých animací dosáhnete pomocí ValueAnimator nebo Choreographer pro synchronizaci s Vsync.
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é