Canvas i Android — är en klass från paketet android.graphics som tillhandahåller API för 2D-ritning på View. Canvas fungerar som en duk på vilken utvecklaren ritar geometriska figurer, text, bilder och sökvägar med hjälp av olika draw*-metoder. Enligt Android Developers Documentation (2025) är Canvas det främsta verktyget för anpassad ritning i Android och används tillsammans med Paint-klassen för att ställa in stilar och färger. Varje anrop till onDraw() får en färdig Canvas-instans kopplad till vyens bitmap-buffert.
Huvudpunkter
Canvas — är den centrala 2D-grafikklassen i Android som tillhandahåller metoder för ritning på en yta. Varje Canvas är kopplad till en Bitmap — ett minnesområde där resultaten av ritoperationer skrivs. När en View ritas om skickar systemet till onDraw() en Canvas som redan är kopplad till denna vy's buffert.
Canvas arbetar i ett koordinatsystem där (0,0) är det övre vänstra hörnet, X-axeln går åt höger och Y-axeln nedåt. Alla ritoperationer använder Canvas aktuella tillstånd, inklusive transformationsmatris, klippområde (clip) och Paint-stil. Enligt Android Documentation (2025) innehåller Canvas API över 30 draw*-metoder för olika typer av grafiska primitiver.
Från och med Android 3.0 (API 11) stöder Canvas hårdvaruacceleration via DisplayList. I accelererat läge skrivs Canvas-operationer till DisplayList och exekveras sedan på GPU:n. Detta ger en betydande prestandaökning men inför begränsningar: vissa Canvas-operationer (clipPath med icke-Path-klipp) stöds inte i accelererat läge.
Canvas tillhandahåller en uppsättning metoder för att rita grundläggande primitiver. Varje metod tar emot geometriska parametrar och ett Paint-objekt som bestämmer den visuella stilen.
| Metod | Syfte | Exempel |
|---|---|---|
| drawRect() | Rita rektangel | canvas.drawRect(10f, 10f, 100f, 100f, paint) |
| drawCircle() | Rita cirkel | canvas.drawCircle(50f, 50f, 30f, paint) |
| drawLine() | Rita linje | canvas.drawLine(0f, 0f, 100f, 100f, paint) |
| drawText() | Rita text | canvas.drawText("Hello", 10f, 20f, paint) |
| drawBitmap() | Rita bild | canvas.drawBitmap(bitmap, 0f, 0f, paint) |
| drawPath() | Rita godtycklig sökväg | canvas.drawPath(path, paint) |
| drawOval() | Rita oval | canvas.drawOval(rectF, paint) |
| drawArc() | Rita båge eller sektor | canvas.drawArc(rectF, 0f, 90f, true, paint) |
Varje metod kan anropas i valfri ordning — Canvas ritar i anropsordningen och lägger nya element ovanpå tidigare. För komplexa kompositioner används tekniken med flerskiktsritning med sparning och återställning av Canvas tillstånd.
Paint — är klassen som bestämmer hur Canvas ska rita. Utan Paint är Canvas-metoderna värdelösa — den ställer in färg, linjetjocklek, fyllningsstil, textstorlek, effekter (skugga, gradient, oskärpa) och blandningsläge.
Paint har viktiga egenskaper: color (färg), style (FILL, STROKE eller FILL_AND_STROKE), strokeWidth (linjetjocklek), textSize (textstorlek), isAntiAlias (kantutjämning), shader (gradient eller bild). Att skapa ett Paint-objekt är en dyr operation, därför bör det skapas en gång och återanvändas.
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)
}
I detta exempel används två Paint: en för att fylla cirkeln med lila, den andra för vit kontur. Uppdelningen i fill och stroke är en standardpraxis som möjliggör flexibel hantering av utseendet utan att återskapa objekt.
För gradientfyllningar accepterar Paint en Shader — underklasser LinearGradient, RadialGradient, SweepGradient. Shader kopplas till Paint via metoden setShader() och bestämmer hur färgen förändras över ritområdet.
Canvas stöder matristransformeringar av koordinatsystemet: translate() (förskjutning), rotate() (rotation), scale() (skalning) och skew() (snedställning). Transformeringar ackumuleras: varje efterföljande anrop appliceras ovanpå tidigare.
Hantering av transformationsstacken sker via save() och restore(). save() sparar Canvas aktuella tillstånd (matris, clip) i stacken, restore() återställer det senast sparade tillståndet. Mellan save() och restore() kan det finnas nästlade transformeringar.
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
canvas.save() // Spara initialt tillstånd
canvas.translate(100f, 100f) // Flytta origo
// Första kvadraten
canvas.drawRect(0f, 0f, 50f, 50f, fillPaint)
canvas.save() // Spara efter flytt
canvas.rotate(45f, 25f, 25f) // Rotera runt kvadratens centrum
// Roterad kvadrat
canvas.drawRect(0f, 0f, 50f, 50f, strokePaint)
canvas.restore() // Återställ efter rotation
canvas.restore() // Återställ initialt tillstånd
}
Använd alltid save() före transformeringar och restore() efter deras slutförande. Obalanserade save/restore leder till ackumulering av matrisen och felaktig ritning av efterföljande element.
För komplex grafik kombineras Canvas med Bitmap och Path. Bitmap används för att ladda bilder och cachelagra renderade ramar. Path — för att bygga godtyckliga Bezier-kurvor, polygoner och komplexa konturer.
Teknik för offscreen-rendering: en Bitmap skapas, en komplex scen ritas på den via en separat Canvas, och sedan visas den färdiga Bitmap i onDraw() via drawBitmap(). Detta gör det möjligt att uppdatera komplex grafik utan att helt rita om varje ram.
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)
// Rendera scen en gång
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 används för att bygga komplexa konturer: Bezier-kurvor (quadTo, cubicTo), bågar (arcTo), linjer (lineTo). Path kan vara sluten (close()) för fyllda former eller öppen för linjer. Huvudmetoderna drawPath() kombinerar Path och Paint för slutlig ritning.
Prestandan för Canvas-ritning påverkar direkt applikationens FPS. Huvudproblem: överdrivna allokeringar i onDraw(), frekventa invalidate() och suboptimal användning av hårdvaruacceleration.
Paint, Path, Rect, RectF och andra objekt bör skapas en gång i initieraren eller setup()-metoden. Varje allokering i onDraw() belastar garbage collection, vilket leder till mikro-hackningar. Undantag — static eller companion object för oföränderliga objekt.
Om en stor del av View är dold utanför det synliga området (t.ex. i ScrollView), använd canvas.clipRect() för att begränsa ritningen. Hårdvaruacceleration optimerar i detta fall ytterligare beskärning av osynliga pixlar.
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
// Begränsa till synligt område
canvas.save()
canvas.clipRect(scrollX, scrollY, scrollX + width, scrollY + height)
// Rendera stor scen
renderScene(canvas)
canvas.restore()
}
Använd View.setLayerType() med LAYER_TYPE_HARDWARE för View med komplex Canvas-grafik som sällan uppdateras. För ofta uppdaterade animationer använder du LAYER_TYPE_NONE för att undvika dyr uppdatering av hårdvarulagret. Profilera genom GPU Profile Rendering och Android Studio Profiler.
Vanliga frågor
Ja, du kan skapa Canvas(Bitmap) — en konstruktor som tar emot Bitmap. Detta används för offscreen-rendering: du ritar på Bitmap och visar den sedan via drawBitmap(). Canvas kan också skapas från Surface (för SurfaceView) eller via Picture.
Canvas — är API för ritning på View i UI-tråden. SurfaceView — är en separat Surface som kan uppdateras från bakgrundstråden via sin egen Canvas (lockCanvas() / unlockCanvasAndPost()). SurfaceView är att föredra för spel och video.
Den vanligaste orsaken är obalanserade save() och restore(). Om restore() inte anropas efter transformering (translate, rotate) förblir Canvas i det ändrade koordinatsystemet och efterföljande element ritas med förskjutning. Kontrollera save()/restore()-paren.
Använd Paint.setTextAlign(Paint.Align.CENTER) för horisontell justering och Paint.getTextBounds() eller Paint.descent() / ascent() för vertikal. Centrering: canvas.drawText(text, centerX, centerY - (ascent + descent) / 2, paint).
Canvas i sig självt animerar inte — det ritar bara det aktuella tillståndet. Animation uppnås genom sekventiella anrop till invalidate() (eller postInvalidateOnAnimation()), varje gång ritas en ny bildruta i onDraw(). För jämna animationer använder du ValueAnimator eller Choreographer för synkronisering med Vsync.
Sammanfattning
Vi utvecklar en mobil applikation nyckelfärdigt
IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.
Läs också