Canvas в Android: какво е това, клас Canvas и методи draw

Автор: IT Sectr Публикувано: 2026-07-21 Време за четене: 7 мин

Canvas в Android — е клас от пакета android.graphics, предоставящ API за 2D рисуване върху View. Canvas работи като платно, върху което разработчикът рисува геометрични фигури, текст, изображения и пътища с помощта на различни методи draw*. Според Android Developers Documentation (2025), Canvas е основният инструмент за персонализирано рисуване в Android и се използва заедно с класа Paint за задаване на стилове и цветове. Всяко извикване на onDraw() получава готова инстанция на Canvas, свързана с bitmap буфера на изгледа.

Основни точки

  • Canvas — клас за 2D рисуване в Android, предоставя методи drawRect, drawCircle, drawText, drawBitmap и други
  • Paint — клас, определящ стила на рисуване: цвят, дебелина, запълване, ефекти (сянка, градиент)
  • onDraw(Canvas) — метод на View, в който се извършва цялото рисуване чрез получения Canvas
  • Canvas.save() и restore() — управление на стека от трансформации за вложени преобразувания на координатната система
  • Path — клас за изграждане на произволни контури и криви, предава се на drawPath(Path, Paint)

Какво е Canvas в Android

Canvas — е централният клас за 2D графика в Android, който предоставя методи за рисуване върху равнина. Всеки Canvas е свързан с Bitmap — област от паметта, в която се записват резултатите от операциите по рисуване. Когато View се прерисува, системата предава на onDraw() Canvas, който вече е свързан с буфера на този изглед.

Canvas работи в координатна система, където (0,0) е горният ляв ъгъл, оста X отива надясно, оста Y надолу. Всички операции по рисуване използват текущото състояние на Canvas, включително трансформационната матрица, зоната на изрязване (clip) и стила Paint. Според Android Documentation (2025), Canvas API включва над 30 метода draw* за различни типове графични примитиви.

Canvas и хардуерно ускорение

От Android 3.0 (API 11) Canvas поддържа хардуерно ускорение чрез DisplayList. В ускорен режим операциите на Canvas се записват в DisplayList и след това се изпълняват на GPU. Това дава значително увеличение на производителността, но налага ограничения: някои операции на Canvas (clipPath с не-Path клип) не се поддържат в ускорен режим.

Основни методи на Canvas за рисуване

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: настройка на стилове и ефекти

Paint — е класът, който определя как Canvas ще рисува. Без Paint методите на Canvas са безполезни — той задава цвят, дебелина на линията, стил на запълване, размер на текста, ефекти (сянка, градиент, размазване) и режим на смесване.

Paint има ключови свойства: color (цвят), style (FILL, STROKE или FILL_AND_STROKE), strokeWidth (дебелина на линията), textSize (размер на текста), isAntiAlias (изглаждане), shader (градиент или изображение). Създаването на обект Paint е скъпа операция, затова трябва да се създаде веднъж и да се преизползва.

kotlin
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 е стандартна практика, позволяваща гъвкаво управление на външния вид без пресъздаване на обекти.

Shader и градиенти

За градиентни запълвания Paint приема Shader — подкласове LinearGradient, RadialGradient, SweepGradient. Shader се прикрепя към Paint чрез метода setShader() и определя как цветът се променя в областта на рисуване.

Трансформации на Canvas: translate, rotate, scale

Canvas поддържа матрични трансформации на координатната система: translate() (преместване), rotate() (завъртане), scale() (мащабиране) и skew() (наклоняване). Трансформациите се натрупват: всяко следващо извикване се прилага върху предишните.

Управлението на стека от трансформации се осъществява чрез save() и restore(). save() запазва текущото състояние на Canvas (матрица, clip) в стека, restore() възстановява последното запазено състояние. Между save() и restore() могат да има вложени трансформации.

kotlin
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 за сложна графика

За сложна графика Canvas се комбинира с Bitmap и Path. Bitmap се използва за зареждане на изображения и кеширане на рендерирани кадри. Path — за изграждане на произволни криви на Безие, многоъгълници и сложни контури.

Offscreen Bitmap буфер

Техника на offscreen-рендериране: създава се Bitmap, върху него се рисува сложна сцена чрез отделен Canvas и след това готовият Bitmap се показва в onDraw() чрез drawBitmap(). Това позволява актуализиране на сложна графика без пълно прерисуване на всеки кадър.

kotlin
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

Производителността на рисуването с Canvas пряко влияе върху FPS на приложението. Основни проблеми: прекомерни алокации в onDraw(), чести invalidate() и неоптимално използване на хардуерното ускорение.

Създавайте обекти извън onDraw()

Paint, Path, Rect, RectF и други обекти трябва да се създават еднократно в инициализатора или метода setup(). Всяка алокация в onDraw() натоварва garbage collector, което води до микро-забавяния. Изключение — static или companion object за неизменяеми обекти.

Използвайте clipRect() за ограничаване на областта

Ако голяма част от View е скрита извън видимата област (например в ScrollView), използвайте canvas.clipRect() за ограничаване на рисуването. Хардуерното ускорение в този случай допълнително оптимизира изрязването на невидими пиксели.

kotlin
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 без View?

Да, може да се създаде Canvas(Bitmap) — конструктор, приемащ Bitmap. Това се използва за offscreen-рендериране: рисувате върху Bitmap, след което го показвате чрез drawBitmap(). Canvas може също да бъде създаден от Surface (за SurfaceView) или чрез Picture.

Каква е разликата между Canvas и SurfaceView?

Canvas — е API за рисуване върху View в UI нишката. SurfaceView — е отделен Surface, който може да се актуализира от фонова нишка чрез собствен Canvas (lockCanvas() / unlockCanvasAndPost()). SurfaceView е предпочитан за игри и видео.

Защо Canvas не рисува след трансформация?

Най-честата причина са небалансираните save() и restore(). Ако след трансформация (translate, rotate) не се извика restore(), Canvas остава в променената координатна система и следващите елементи се рисуват с отместване. Проверете двойките save()/restore().

Как да нарисувам текст в центъра на Canvas?

Използвайте Paint.setTextAlign(Paint.Align.CENTER) за хоризонтално подравняване и Paint.getTextBounds() или Paint.descent() / ascent() за вертикално. Центриране: canvas.drawText(text, centerX, centerY - (ascent + descent) / 2, paint).

Поддържа ли Canvas анимация?

Canvas сам по себе си не анимира — той само рисува текущото състояние. Анимацията се постига чрез последователни извиквания на invalidate() (или postInvalidateOnAnimation()), като всеки път се рисува нов кадър в onDraw(). За плавни анимации използвайте ValueAnimator или Choreographer за синхронизация с Vsync.

Резюме

  • Canvas — централен клас за 2D рисуване в Android, предоставя draw* методи за рисуване на примитиви, текст и изображения
  • Paint — определя визуалния стил (цвят, дебелина, запълване, шейдъри); създава се веднъж и се преизползва
  • save() / restore() — управление на стека от трансформации; всяка трансформация трябва да бъде в двойка save/restore
  • Path — изграждане на произволни контури и криви на Безие за сложна векторна графика
  • Offscreen Bitmap — техника за кеширане на сложни сцени чрез Canvas(Bitmap) за намаляване на натоварването на onDraw()
  • Хардуерно ускорение — GPU ускорение за Canvas от Android 3.0, налага ограничения за някои операции (clipPath)
  • Профилиране — GPU Profile Rendering и Android Studio Profiler са задължителни за намиране на тесни места в onDraw()

Ще разработим мобилно приложение под ключ

IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.

Обсъдете проекта

Прочетете също