onDraw() — vad är det, anropsmekanismen och överskrivning av metoden

Författare: IT Sectr Publicerad: 2026-07-22 Lästid: 10 min

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) — View-metod anropad av systemet för att rendera innehåll på Canvas
  • invalidate() — begäran att omrita View, vilket leder till anrop av onDraw i nästa bildcykel
  • Canvas API tillhandahåller metoderna drawCircle, drawBitmap, drawText, drawPath och andra för ritning
  • Android-systemet utför rendering i UI-tråden, därför måste onDraw vara lätt och snabb
  • Hardware acceleration accelererar automatiskt rendering via GPU, från och med Android 3.0 (API 11)

Vad är onDraw()?

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.

View-renderingscykel

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-fas: dispatchDraw och onDraw

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

När systemet anropar onDraw

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

Hårdvaruacceleration

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 API för anpassad grafik

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-metodSyfteViktiga Paint-parametrar
drawCircleRita cirkelcenterX, centerY, radius, paint
drawRectRita rektangelleft, top, right, bottom, paint
drawLineRita linjestartX, startY, endX, endY, paint
drawPathRita godtycklig konturpath, paint
drawBitmapRita bildbitmap, srcRect, dstRect, paint
drawTextRita texttext, x, y, paint
drawArcRita båge eller sektorovalRect, startAngle, sweepAngle, useCenter, paint
drawOvalRita ovalovalRect, paint

Paint-objekt

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.

Koordinater och transformationer

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.

Prestanda för onDraw

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.

Regler för prestandastark onDraw

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.

Användning av clipRect för optimering

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.

Layer — spara och återställa

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 och postInvalidate

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.

Kodexempel i Kotlin

Låt oss titta på praktiska exempel på överskrivning av onDraw för att skapa anpassade View i Android med Kotlin.

Anpassad View — cirkulär förloppsindikator

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.

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

Anpassad View — övertoningsbakgrund med text

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.

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

Animerad klocka med invalidate

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.

kotlin
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

Kan onDraw anropas direkt?

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.

Varför anropas inte onDraw efter invalidate?

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.

Hur ofta kan onDraw anropas?

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.

Vad är skillnaden mellan onDraw och dispatchDraw?

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.

Kan Canvas användas utanför onDraw?

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

  • onDraw(Canvas) — viktigaste View-metoden för anpassad rendering med Canvas API i Android
  • Renderingscykeln består av faserna measure, layout och draw — onDraw anropas i draw-fasen
  • Canvas API inkluderar metoderna drawCircle, drawRect, drawPath, drawBitmap, drawText och andra
  • invalidate() begär omritning från UI-tråden, postInvalidate() — från bakgrundstråden
  • Partiell omritning via invalidate(Rect) optimerar rendering genom att endast uppdatera dirty-området
  • Prestandan för onDraw är kritisk: att skapa objekt inuti metoden orsakar GC och bildhopp
  • Hårdvaruacceleration (från API 11) accelererar automatiskt Canvas-operationer via GPU

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.

Diskutera projektet

Läs också