Canvas у Android-у — је класа из пакета android.graphics која пружа API за 2D цртање на View. Canvas функционише као платно на којем програмер црта геометријске фигуре, текст, слике и путање помоћу различитих draw* метода. Према Android Developers Documentation (2025), Canvas је основни алат за прилагођено цртање у Android-у и користи се заједно са класом Paint за задавање стилова и боја. Сваки позив onDraw() добија готову инстанцу Canvas повезану са bitmap бафером прегледа.
Главно
Canvas — је централна класа 2D графике у Android-у која пружа методе за цртање на равни. Сваки Canvas је повезан са Bitmap — меморијском облашћу у коју се уписују резултати операција цртања. Када се View поново исцртава, систем прослеђује у onDraw() Canvas који је већ повезан са бафером тог View.
Canvas ради у координатном систему где је (0,0) горњи леви угао, оса X иде удесно, оса Y наниже. Све операције цртања користе тренутно стање Canvas-а, укључујући матрицу трансформације, област сечења (clip) и стил Paint. Према Android Documentation (2025), Canvas API укључује преко 30 draw* метода за различите типове графичких примитива.
Од Android 3.0 (API 11) Canvas подржава хардверско убрзање кроз DisplayList. У убрзаном режиму, Canvas операције се записују у DisplayList и затим извршавају на GPU-у. Ово даје значајан пораст перформанси, али намеће ограничења: неке Canvas операције (clipPath са не-Path клипом) нису подржане у убрзаном режиму.
Canvas пружа скуп метода за цртање основних примитива. Сваки метод прима геометријске параметре и објекат Paint који одређује визуелни стил.
| Метод | Намена | Пример |
|---|---|---|
| drawRect() | Цртање правоугаоника | canvas.drawRect(10f, 10f, 100f, 100f, paint) |
| drawCircle() | Цртање круга | canvas.drawCircle(50f, 50f, 30f, paint) |
| drawLine() | Цртање линије | canvas.drawLine(0f, 0f, 100f, 100f, paint) |
| drawText() | Цртање текста | canvas.drawText("Hello", 10f, 20f, paint) |
| drawBitmap() | Цртање слике | canvas.drawBitmap(bitmap, 0f, 0f, paint) |
| drawPath() | Цртање произвољне путање | canvas.drawPath(path, paint) |
| drawOval() | Цртање овала | canvas.drawOval(rectF, paint) |
| drawArc() | Цртање лука или сектора | canvas.drawArc(rectF, 0f, 90f, true, paint) |
Сваки метод може бити позван у било ком редоследу — Canvas црта по редоследу позива, постављајући нове елементе преко претходних. За сложене композиције користи се техника вишеслојног цртања са чувањем и враћањем стања Canvas-а.
Paint — је класа која одређује како ће Canvas цртати. Без Paint-а, Canvas методи су бескорисни — он задаје боју, дебљину линије, стил попуне, величину текста, ефекте (сенку, градијент, замућење) и режим преклапања.
Paint има кључна својства: color (боја), style (FILL, STROKE или FILL_AND_STROKE), strokeWidth (дебљина линије), textSize (величина текста), isAntiAlias (изглађивање), shader (градијент или слика). Креирање објекта Paint је скупа операција, зато га треба креирати једном и поново користити.
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)
}
У овом примеру се користе два Paint: један за попуну круга љубичастом бојом, други за бели обрис. Подела на fill и stroke је стандардна пракса која омогућава флексибилно управљање изгледом без поновног креирања објеката.
За градијентне попуне Paint прима Shader — подкласе LinearGradient, RadialGradient, SweepGradient. Shader се везује за Paint кроз метод setShader() и одређује како се боја мења по области цртања.
Canvas подржава матричне трансформације координатног система: translate() (померање), rotate() (ротација), scale() (скалирање) и skew() (нагињање). Трансформације се акумулирају: сваки наредни позив се примењује на претходне.
Управљање стеком трансформација се врши кроз save() и restore(). save() чува тренутно стање Canvas-а (матрицу, clip) на стек, restore() враћа последње сачувано стање. Између save() и restore() могу бити угнежђене трансформације.
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
canvas.save() // Сачувај почетно стање
canvas.translate(100f, 100f) // Помери координатни почетак
// Први квадрат
canvas.drawRect(0f, 0f, 50f, 50f, fillPaint)
canvas.save() // Сачувај након померања
canvas.rotate(45f, 25f, 25f) // Ротирај око центра квадрата
// Ротирани квадрат
canvas.drawRect(0f, 0f, 50f, 50f, strokePaint)
canvas.restore() // Врати након ротације
canvas.restore() // Врати почетно стање
}
Увек користите save() пре почетка трансформација и restore() након њиховог завршетка. Неуравнотежени save/restore доводе до акумулације матрице и неправилног цртања наредних елемената.
За сложену графику, Canvas се комбинује са Bitmap и Path. Bitmap се користи за учитавање слика и кеширање рендерованих оквира. Path — за изградњу произвољних Безијеових кривих, полигона и сложених контура.
Техника offscreen-рендеровања: креира се Bitmap, на њему се црта сложена сцена кроз посебан Canvas, а затим се готов Bitmap приказује у onDraw() кроз drawBitmap(). Ово омогућава ажурирање сложене графике без потпуног прецртавања сваког оквира.
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)
// Рендеруј сцену једном
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 се користи за изградњу сложених контура: Безијеове криве (quadTo, cubicTo), лукови (arcTo), линије (lineTo). Path може бити затворен (close()) за попуњене фигуре или отворен за линије. Главни методи drawPath() комбинују Path и Paint за коначно цртање.
Перформансе Canvas цртања директно утичу на FPS апликације. Главни проблеми: прекомерне алокације у onDraw(), чести invalidate() и неоптимално коришћење хардверског убрзања.
Paint, Path, Rect, RectF и други објекти треба да се креирају једном у иницијализатору или методи setup(). Свака алокација у onDraw() ствара оптерећење на garbage collector, што доводи до микро-застоја. Изузетак — static или companion object за immutable објекте.
Ако је велики део View сакривен ван видљиве области (нпр. у ScrollView), користите canvas.clipRect() за ограничавање цртања. Хардверско убрзање у овом случају додатно оптимизује сечење невидљивих пиксела.
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
// Исеци на видљиву област
canvas.save()
canvas.clipRect(scrollX, scrollY, scrollX + width, scrollY + height)
// Рендеруј велику сцену
renderScene(canvas)
canvas.restore()
}
Користите View.setLayerType() са LAYER_TYPE_HARDWARE за View са сложеном Canvas графиком која се ретко ажурира. За често ажуриране анимације користите LAYER_TYPE_NONE да бисте избегли скупо ажурирање хардверског слоја. Профилишите кроз GPU Profile Rendering и Android Studio Profiler.
Често постављана питања
Да, може се креирати Canvas(Bitmap) — конструктор који прима Bitmap. Ово се користи за offscreen-рендеровање: цртате на Bitmap, а затим га приказујете кроз drawBitmap(). Canvas такође може бити креиран из Surface (за SurfaceView) или кроз Picture.
Canvas — је API за цртање на View у UI нити. SurfaceView — је засебан Surface који се може ажурирати из позадинске нити кроз сопствени Canvas (lockCanvas() / unlockCanvasAndPost()). SurfaceView је пожељнији за игре и видео.
Најчешћи узрок су неуравнотежени save() и restore(). Ако након трансформације (translate, rotate) не позовете restore(), Canvas остаје у измењеном координатном систему, а наредни елементи се цртају са померајем. Проверите парове save()/restore().
Користите Paint.setTextAlign(Paint.Align.CENTER) за хоризонтално поравнање и Paint.getTextBounds() или Paint.descent() / ascent() за вертикално. Центрирање: canvas.drawText(text, centerX, centerY - (ascent + descent) / 2, paint).
Canvas сам по себи не анимира — он само црта тренутно стање. Анимација се постиже кроз узастопне позиве invalidate() (или postInvalidateOnAnimation()), сваки пут цртајући нови оквир у onDraw(). За глатке анимације користите ValueAnimator или Choreographer за синхронизацију са Vsync.
Закључак
Развићемо мобилну апликацију под кључ
IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође