Canvas w Android: co to jest, klasa Canvas i metody draw

Autor: IT Sectr Opublikowano: 2026-07-21 Czas czytania: 7 min

Canvas w Android — to klasa z pakietu android.graphics, udostępniająca API do rysowania 2D na View. Canvas działa jak płótno, na którym programista rysuje figury geometryczne, tekst, obrazy i ścieżki za pomocą różnych metod draw*. Według Android Developers Documentation (2025), Canvas jest głównym narzędziem do niestandardowego rysowania w Android i jest używany razem z klasą Paint do ustawiania stylów i kolorów. Każde wywołanie onDraw() otrzymuje gotową instancję Canvas powiązaną z buforem bitmap widoku.

Najważniejsze

  • Canvas — klasa do rysowania 2D w Android, udostępnia metody drawRect, drawCircle, drawText, drawBitmap i inne
  • Paint — klasa określająca styl rysowania: kolor, grubość, wypełnienie, efekty (cień, gradient)
  • onDraw(Canvas) — metoda View, w której odbywa się całe rysowanie przez otrzymany Canvas
  • Canvas.save() i restore() — zarządzanie stosem transformacji dla zagnieżdżonych przekształceń układu współrzędnych
  • Path — klasa do budowania dowolnych konturów i krzywych, przekazywana do drawPath(Path, Paint)

Czym jest Canvas w Android

Canvas — to centralna klasa grafiki 2D w Android, która udostępnia metody do rysowania na płaszczyźnie. Każdy Canvas jest powiązany z Bitmap — obszarem pamięci, w którym zapisywane są wyniki operacji rysowania. Gdy View jest przerysowywany, system przekazuje do onDraw() Canvas już powiązany z buforem tego View.

Canvas działa w układzie współrzędnych, gdzie (0,0) to lewy górny róg, oś X idzie w prawo, oś Y w dół. Wszystkie operacje rysowania wykorzystują bieżący stan Canvas, w tym macierz transformacji, obszar przycięcia (clip) i styl Paint. Według Android Documentation (2025), API Canvas zawiera ponad 30 metod draw* dla różnych typów prymitywów graficznych.

Canvas i Hardware Acceleration

Od Androida 3.0 (API 11) Canvas obsługuje przyspieszenie sprzętowe przez DisplayList. W trybie przyspieszonym operacje Canvas są zapisywane do DisplayList, a następnie wykonywane na GPU. Daje to znaczący wzrost wydajności, ale nakłada ograniczenia: niektóre operacje Canvas (clipPath z nie-Path-clipem) nie są obsługiwane w trybie przyspieszonym.

Podstawowe metody Canvas do rysowania

Canvas udostępnia zestaw metod do rysowania podstawowych prymitywów. Każda metoda przyjmuje parametry geometryczne i obiekt Paint, określający styl wizualny.

MetodaPrzeznaczeniePrzykład
drawRect()Rysowanie prostokątacanvas.drawRect(10f, 10f, 100f, 100f, paint)
drawCircle()Rysowanie kołacanvas.drawCircle(50f, 50f, 30f, paint)
drawLine()Rysowanie liniicanvas.drawLine(0f, 0f, 100f, 100f, paint)
drawText()Rysowanie tekstucanvas.drawText("Hello", 10f, 20f, paint)
drawBitmap()Rysowanie obrazucanvas.drawBitmap(bitmap, 0f, 0f, paint)
drawPath()Rysowanie dowolnej ścieżkicanvas.drawPath(path, paint)
drawOval()Rysowanie owalucanvas.drawOval(rectF, paint)
drawArc()Rysowanie łuku lub sektoracanvas.drawArc(rectF, 0f, 90f, true, paint)

Każda metoda może być wywołana w dowolnej kolejności — Canvas rysuje w kolejności wywołań, nakładając nowe elementy na poprzednie. W przypadku złożonych kompozycji stosuje się technikę rysowania wielowarstwowego z zapisywaniem i przywracaniem stanu Canvas.

Klasa Paint: konfiguracja stylów i efektów

Paint — to klasa, która określa, jak Canvas będzie rysować. Bez Paint metody Canvas są bezużyteczne — ustawia on kolor, grubość linii, styl wypełnienia, rozmiar tekstu, efekty (cień, gradient, rozmycie) i tryb mieszania.

Paint ma kluczowe właściwości: color (kolor), style (FILL, STROKE lub FILL_AND_STROKE), strokeWidth (grubość linii), textSize (rozmiar tekstu), isAntiAlias (wygładzanie), shader (gradient lub obraz). Tworzenie obiektu Paint jest kosztowną operacją, dlatego należy go tworzyć raz i wielokrotnie używać.

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

W tym przykładzie używane są dwa Paint: jeden do wypełnienia koła kolorem fioletowym, drugi do białego obrysu. Podział na fill i stroke to standardowa praktyka, pozwalająca elastycznie zarządzać wyglądem bez odtwarzania obiektów.

Shader i gradienty

Do wypełnień gradientowych Paint przyjmuje Shader — podklasy LinearGradient, RadialGradient, SweepGradient. Shader jest przypisywany do Paint przez metodę setShader() i określa, jak kolor zmienia się w obszarze rysowania.

Transformacje Canvas: translate, rotate, scale

Canvas obsługuje macierzowe transformacje układu współrzędnych: translate() (przesunięcie), rotate() (obrót), scale() (skalowanie) i skew() (pochylenie). Transformacje kumulują się: każde kolejne wywołanie jest stosowane na poprzednich.

Zarządzanie stosem transformacji odbywa się przez save() i restore(). save() zapisuje bieżący stan Canvas (macierz, clip) na stosie, restore() przywraca ostatni zapisany stan. Między save() a restore() mogą znajdować się zagnieżdżone transformacje.

kotlin
override fun onDraw(canvas: Canvas) {
    super.onDraw(canvas)

    canvas.save() // Zapisz stan początkowy
    canvas.translate(100f, 100f) // Przesuń początek układu

    // Pierwszy kwadrat
    canvas.drawRect(0f, 0f, 50f, 50f, fillPaint)

    canvas.save() // Zapisz po przesunięciu
    canvas.rotate(45f, 25f, 25f) // Obróć wokół środka kwadratu

    // Obrócony kwadrat
    canvas.drawRect(0f, 0f, 50f, 50f, strokePaint)
    canvas.restore() // Przywróć po obrocie

    canvas.restore() // Przywróć stan początkowy
}

Zawsze używaj save() przed rozpoczęciem transformacji i restore() po ich zakończeniu. Niezrównoważone save/restore prowadzą do kumulacji macierzy i nieprawidłowego rysowania kolejnych elementów.

Canvas z Bitmap i Path dla złożonej grafiki

W przypadku złożonej grafiki Canvas jest łączony z Bitmap i Path. Bitmap służy do ładowania obrazów i buforowania wyrenderowanych klatek. Path — do budowania dowolnych krzywych Beziera, wielokątów i złożonych konturów.

Offscreen Bitmap bufor

Technika offscreen-renderowania: tworzona jest Bitmap, na której rysowana jest złożona scena przez osobny Canvas, a następnie gotowa Bitmap jest wyświetlana w onDraw() przez drawBitmap(). Pozwala to aktualizować złożoną grafikę bez pełnego przerysowywania każdej klatki.

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)

    // Renderuj scenę raz
    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 jest używany do budowania złożonych konturów: krzywe Beziera (quadTo, cubicTo), łuki (arcTo), linie (lineTo). Path może być zamknięty (close()) dla wypełnionych figur lub otwarty dla linii. Główne metody drawPath() łączą Path i Paint do końcowego rysowania.

Optymalizacja rysowania Canvas

Wydajność rysowania Canvas bezpośrednio wpływa na FPS aplikacji. Główne problemy: nadmierne alokacje w onDraw(), częste invalidate() i nieoptymalne wykorzystanie Hardware Acceleration.

Twórz obiekty poza onDraw()

Paint, Path, Rect, RectF i inne obiekty powinny być tworzone jednorazowo w inicjalizatorze lub metodzie setup(). Każda alokacja w onDraw() obciąża garbage collector, co prowadzi do mikrozacięć. Wyjątkiem są static lub companion object dla obiektów immutable.

Używaj clipRect() do ograniczania obszaru

Jeśli duża część View jest ukryta poza widocznym obszarem (np. w ScrollView), używaj canvas.clipRect() do ograniczenia rysowania. Hardware Acceleration w tym przypadku dodatkowo optymalizuje przycinanie niewidocznych pikseli.

kotlin
override fun onDraw(canvas: Canvas) {
    super.onDraw(canvas)

    // Przytnij do widocznego obszaru
    canvas.save()
    canvas.clipRect(scrollX, scrollY, scrollX + width, scrollY + height)

    // Renderuj dużą scenę
    renderScene(canvas)
    canvas.restore()
}

Używaj View.setLayerType() z LAYER_TYPE_HARDWARE dla View ze złożoną grafiką Canvas, która aktualizuje się rzadko. W przypadku często aktualizowanych animacji używaj LAYER_TYPE_NONE, aby uniknąć kosztownej aktualizacji warstwy sprzętowej. Profiluj przez GPU Profile Rendering i Android Studio Profiler.

Często zadawane pytania

Czy można utworzyć Canvas bez View?

Tak, można utworzyć Canvas(Bitmap) — konstruktor przyjmujący Bitmap. Jest to używane do offscreen-renderowania: rysujesz na Bitmap, a następnie wyświetlasz ją przez drawBitmap(). Canvas może być również utworzony z Surface (dla SurfaceView) lub przez Picture.

Czym różni się Canvas od SurfaceView?

Canvas — to API do rysowania na View w wątku UI. SurfaceView — to osobny Surface, który może być aktualizowany z wątku tła przez własny Canvas (lockCanvas() / unlockCanvasAndPost()). SurfaceView jest preferowany do gier i wideo.

Dlaczego Canvas nie rysuje po transformacji?

Najczęstszą przyczyną są niezrównoważone save() i restore(). Jeśli po transformacji (translate, rotate) nie wywołasz restore(), Canvas pozostaje w zmienionym układzie współrzędnych, a kolejne elementy są rysowane z przesunięciem. Sprawdź pary save()/restore().

Jak narysować tekst na środku Canvas?

Używaj Paint.setTextAlign(Paint.Align.CENTER) do wyrównania poziomego oraz Paint.getTextBounds() lub Paint.descent() / ascent() do pionowego. Wyśrodkowanie: canvas.drawText(text, centerX, centerY - (ascent + descent) / 2, paint).

Czy Canvas obsługuje animację?

Canvas sam w sobie nie animuje — on tylko rysuje bieżący stan. Animacja osiągana jest przez sekwencyjne wywołania invalidate() (lub postInvalidateOnAnimation()), każdorazowo rysując nową klatkę w onDraw(). Do płynnych animacji używaj ValueAnimator lub Choreographer do synchronizacji z Vsync.

Podsumowanie

  • Canvas — centralna klasa rysowania 2D w Android, udostępnia metody draw* do rysowania prymitywów, tekstu i obrazów
  • Paint — określa styl wizualny (kolor, grubość, wypełnienie, shadery); tworzony raz i wielokrotnie używany
  • save() / restore() — zarządzanie stosem transformacji; każda transformacja powinna być w parze save/restore
  • Path — budowanie dowolnych konturów i krzywych Beziera dla złożonej grafiki wektorowej
  • Offscreen Bitmap — technika buforowania złożonych scen przez Canvas(Bitmap) w celu zmniejszenia obciążenia onDraw()
  • Hardware Acceleration — przyspieszenie GPU dla Canvas od Androida 3.0, nakłada ograniczenia na niektóre operacje (clipPath)
  • Profilowanie — GPU Profile Rendering i Android Studio Profiler są obowiązkowe do znajdowania wąskich gardeł w onDraw()

Opracujemy aplikację mobilną pod klucz

IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.

Omów projekt

Przeczytaj również