onDraw() — är den viktigaste metoden i klassen View i Android SDK, ansvarig för att rendera innehållet i vyn. Systemet anropar onDraw vid varje uppdatering av View:s utseende — vid initial rendering, efter invalidate() eller vid ändring av dimensioner. Enligt Android Developers Documentation (2026) är överskrivning av onDraw med Canvas API det primära sättet att skapa anpassad grafik i Android-applikationer.
Huvudpunkter
onDraw(Canvas canvas) — är en protected metod i klassen android.view.View, som anropas av Android-systemet när View måste renderas. Utvecklaren överskriver denna metod för att implementera anpassad rendering: former, text, bilder, övertoningar, animation. Parametern Canvas är den grafiska yta på vilken alla ritoperationer utförs.
Metodens signatur är enkel: override fun onDraw(canvas: Canvas). Inuti denna metod får man inte skapa nya objekt (för att undvika GC-belastning), utföra långa operationer eller arbeta med nätverket. View innehåller också metoden onDraw i sin livscykel mellan onLayout (beräkning av positioner) och dispatchDraw (rendering av underordnade element).
Vid arv från View är överskrivning av onDraw obligatorisk för att visa något innehåll — annars kommer View att vara tom. Vid arv från befintliga underklasser (TextView, ImageView, Button) är överskrivning av onDraw valfri och används för att lägga till extra grafik ovanpå standardinnehållet.
Android utför rendering av View i en strikt definierad ordning, anropad från huvudtråden (UI-tråden). Hela cykeln består av tre faser: measure (mätning av dimensioner), layout (beräkning av positioner) och draw (rendering). onDraw är en del av draw-fasen och anropas efter att View:s dimensioner och position har bestämts.
draw(Canvas canvas) — är den publika metoden i View som anropar onDraw och dispatchDraw. Metoden draw är inte avsedd att överskrivas — istället överskrivs onDraw. Efter exekvering av onDraw för den aktuella View anropas dispatchDraw, som rekursivt anropar draw för alla underordnade View (om den aktuella View är en ViewGroup-behållare).
onDraw anropas under följande förhållanden: 1) View blir synlig för första gången (efter setVisibility(VISIBLE)); 2) applikationen anropar invalidate() för denna View eller dess förälder; 3) View:s storlek ändras (ny layout); 4) animationen som är kopplad till View ändras (via ValueAnimator eller ObjectAnimator); 5) View:s tillstånd ändras (pressed, focused, enabled).
Från Android 3.0 (API 11) utförs de flesta Canvas-operationer med hårdvaruacceleration via GPU. Utvecklaren kan kontrollera om acceleration är aktiverad via canvas.isHardwareAccelerated(). Vid hårdvaruacceleration stöds vissa Canvas-operationer inte (t.ex. clipPath med icke-rektangulärt område) och kan ignoreras eller orsaka fel.
Canvas tillhandahåller en rik uppsättning metoder för ritning. Alla metoder arbetar i det aktuella koordinatsystemet, som kan ändras genom transformationer (translate, rotate, scale, skew). Varje metod accepterar ett Paint-objekt som definierar färg, stil, linjetjocklek, effekter och kantutjämning.
| Canvas-metod | Syfte | Viktiga Paint-parametrar |
|---|---|---|
| drawCircle | Rita cirkel | centerX, centerY, radius, paint |
| drawRect | Rita rektangel | left, top, right, bottom, paint |
| drawLine | Rita linje | startX, startY, endX, endY, paint |
| drawPath | Rita godtycklig kontur | path, paint |
| drawBitmap | Rita bild | bitmap, srcRect, dstRect, paint |
| drawText | Rita text | text, x, y, paint |
| drawArc | Rita båge eller sektor | ovalRect, startAngle, sweepAngle, useCenter, paint |
| drawOval | Rita oval | ovalRect, paint |
Paint definierar renderingsstilen: färg (setColor), linjetjocklek (setStrokeWidth), fyllnadsstil (setStyle — FILL, STROKE, FILL_AND_STROKE), kantutjämning (setAntiAlias), effekter (setShadowLayer, setMaskFilter) och övertoningar (setShader). Det rekommenderas att skapa och konfigurera Paint en gång i View-konstruktorn, inte inuti onDraw, för att undvika att skapa objekt i renderingscykeln.
Canvas använder ett koordinatsystem där X-axeln är riktad åt höger och Y-axeln nedåt. Koordinatursprunget (0, 0) är View:s övre vänstra hörn. Innan anrop av draw-metoder kan utvecklaren ändra koordinatsystemet via canvas.translate(dx, dy), canvas.rotate(degrees, px, py), canvas.scale(sx, sy, px, py). Alla efterföljande ritoperationer kommer att utföras i det transformerade systemet.
onDraw exekveras i huvud-UI-tråden och dess varaktighet påverkar direkt bildfrekvensen (fps). För att upprätthålla 60 fps måste metoden onDraw slutföras inom högst 16 millisekunder. All fördröjning leder till bildhopp (jank), vilket visuellt uppfattas som ryckig animation eller fördröjning vid scrollning.
Grundläggande regler för optimering av onDraw inkluderar: 1) skapa inte objekt inuti onDraw — alla Paint, Path, Rect bör vara klassfält; 2) allokera inte minne (new) — detta utlöser skräpinsamling (GC) och bildhopp; 3) cachelagra komplexa operationer i Bitmap via Bitmap.createBitmap och rita den förberedda Bitmapen; 4) utför inte in-ut-operationer eller laddning av resurser.
clipRect begränsar ritområdet till en specificerad rektangel. Används för att Canvas inte ska slösa tid på att rendera pixlar utanför det synliga området. Särskilt effektivt vid scrollning av stora listor eller skalning: ställ in clipRect på det synliga området (dirty rect), och Canvas kommer bara att rita det som är synligt.
canvas.save() sparar det aktuella tillståndet för Canvas (transformation och clip) på stacken. canvas.restore() återställer det senast sparade tillståndet. Detta är nödvändigt för tillfälliga transformationer: spara tillståndet, applicera translate/rotate, rita, återställ sedan. save och restore är lätta och påverkar inte prestandan vid rimlig användning (mindre än 10–15 nivåer av nästling).
invalidate() — är View-metoden som informerar systemet att View:s innehåll är föråldrat och behöver omritas. Anrop av invalidate initierar en ny draw-cykel där onDraw kommer att anropas. invalidate bör endast anropas från UI-tråden (main thread). För anrop från bakgrundstråden används postInvalidate.
postInvalidate() — den trådsäkra versionen av invalidate, som skickar begäran om omritning till huvudtrådens kö via Handler. Används i bakgrundsuppgifter när View behöver uppdateras efter slutförd dataladdning eller beräkningar. Kombineras med runOnUiThread för synkronisering med UI.
För partiell omritning används invalidate(Rect dirty) eller invalidate(int l, int t, int r, int b). Detta indikerar för systemet att endast det specificerade området har ändrats, inte hela View. Android optimerar rendering genom att endast omrita dirty rect, vilket avsevärt förbättrar prestandan vid animationer med partiella uppdateringar.
Låt oss titta på praktiska exempel på överskrivning av onDraw för att skapa anpassade View i Android med Kotlin.
Exemplet skapar en cirkulär förloppsindikator med animerad fyllning. onDraw ritar två cirklar: en grå bakgrund och en färgad fyllning, vars båge bestäms av det aktuella förloppet. Anrop av invalidate i setProgress säkerställer omritning när värdet ändras.
class CircularProgressView
@JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null
) : View(context, attrs) {
private val bgPaint = Paint().apply {
style = Paint.Style.STROKE
strokeWidth = 12f
color = Color.parseColor("#E0E0E0")
isAntiAlias = true
}
private val progressPaint = Paint().apply {
style = Paint.Style.STROKE
strokeWidth = 12f
color = Color.BLUE
isAntiAlias = true
strokeCap = Paint.Cap.ROUND
}
var progress: Float = 0f
set(value) {
field = value.coerceIn(0f, 1f)
invalidate()
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
val size = minOf(width, height) - 24f
val oval = RectF(12f, 12f,
12f + size, 12f + size)
canvas.drawArc(oval, 270f, 360f, false, bgPaint)
canvas.drawArc(oval, 270f,
progress * 360f,
false, progressPaint)
}
}
Exemplet ritar en rektangel med övertoning och text ovanpå den. Övertoningen skapas via LinearGradient (Shader) och texten via drawText. Båda Paint skapas en gång i konstruktorn, och i onDraw ändras endast rektangelns storlek beroende på View:s aktuella bredd.
class GradientTextView(context: Context)
: View(context) {
private val shaderPaint = Paint().apply {
isAntiAlias = true
}
private val textPaint = Paint().apply {
color = Color.WHITE
textSize = 48f
isAntiAlias = true
textAlign = Paint.Align.CENTER
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
val gradient = LinearGradient(
0f, 0f, width.toFloat(), 0f,
Color.parseColor("#FF6B35"),
Color.parseColor("#FF0040"),
Shader.TileMode.CLAMP
)
shaderPaint.shader = gradient
canvas.drawRect(0f, 0f,
width.toFloat(),
height.toFloat(), shaderPaint)
val xCenter = width / 2f
val yCenter = height / 2f
val yOffset = (textPaint.descent() +
textPaint.ascent()) / 2f
canvas.drawText("Hej från Canvas",
xCenter, yCenter - yOffset,
textPaint)
}
}
Exemplet skapar en animerad klocka som uppdaterar renderingen var 1000:e millisekund. postDelayed i onDraw anropar invalidate efter en sekund, vilket skapar en oändlig omritningscykel. Klockans visare ritas via drawLine med olika längd och tjocklek.
class AnalogClockView(context: Context)
: View(context) {
private val handPaint = Paint().apply {
style = Paint.Style.STROKE
isAntiAlias = true
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
val cx = width / 2f
val cy = height / 2f
val radius = minOf(cx, cy) - 20f
val now = Calendar.getInstance()
val hours = now.get(Calendar.HOUR) % 12
val minutes = now.get(Calendar.MINUTE)
val seconds = now.get(Calendar.SECOND)
val secondAngle = Math.toRadians(
seconds * 6f - 90f)
handPaint.strokeWidth = 2f
handPaint.color = Color.RED
canvas.drawLine(cx, cy,
cx + cos(secondAngle) * radius * 0.8f,
cy + sin(secondAngle) * radius * 0.8f,
handPaint)
postDelayed({ invalidate() }, 1000)
}
}
Vanliga frågor
Nej, onDraw bör inte anropas direkt. För begäran om omritning, använd invalidate() (från UI-tråden) eller postInvalidate() (från bakgrundstråden). Systemet bestämmer själv när onDraw ska anropas och optimerar rendering genom att slå samman flera invalidate-anrop i en enda genomgång.
Orsaker: View är dold (setVisibility(GONE) eller INVISIBLE); View har inte lagts till i Window-hierarkin; View-storleken är 0 (width eller height = 0); setWillNotDraw(true) anropas (standard true för ViewGroup). Kontrollera var och en av dessa punkter vid felsökning.
Vid 60 fps kan onDraw anropas upp till 60 gånger per sekund. Vid 120 fps (på enheter med 120 Hz-skärmar) — upp till 120 gånger. Varje anrop måste slutföras inom ~8–16 millisekunder. Vid aktiva animationer via ObjectAnimator eller ValueAnimator bestäms frekvensen av antalet animationsbildrutor.
onDraw ansvarar för rendering av View:s eget innehåll. dispatchDraw ansvarar för rendering av underordnade View (i ViewGroup). dispatchDraw anropas efter onDraw och går rekursivt igenom alla underordnade element. För en vanlig View (inte ViewGroup) är dispatchDraw tom.
Ja, Canvas kan skapas för Bitmap via Canvas(bitmap) och man kan rita på det i vilken tråd som helst. Detta används för förberäkning av komplex grafik i bakgrunden. Canvas som skickas i onDraw tillhör dock systemet och kan endast användas i UI-tråden inuti metoden onDraw.
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å