dispatchDraw(): mi ez, a meghívás mechanizmusa és különbség az onDraw-tól

Szerző: IT Sectr Megjelenés: 2026-07-22 Olvasási idő: 9 perc

dispatchDraw() — a ViewGroup osztály metódusa, amely felelős az összes gyermek View rekurzív rajzolásáért az Android hierarchiában. Az Android Developers Documentation (2026) szerint a dispatchDraw automatikusan meghívásra kerül az onDraw után a fő View draw() metódusában, és végigmegy az összes gyermekelemeken, meghívva azok saját draw() metódusát. A fejlesztők azért írják felül a dispatchDraw-ot egyedi ViewGroup-okban, hogy effekteket adjanak hozzá, grafikát helyezzenek a gyermekek fölé, vagy megváltoztassák a rajzolási sorrendet.

Főbb pontok

  • dispatchDraw(Canvas canvas) — a ViewGroup protected metódusa, amely az onDraw után hívódik meg a gyermek View-k rajzolásához
  • draw(Canvas canvas) — a View public final metódusa, amely egymás után meghívja az onDraw, dispatchDraw és onDrawForeground metódusokat
  • onDraw a View saját tartalmát rajzolja, dispatchDraw — az összes gyermekelem tartalmát
  • setWillNotDraw(true) — optimalizáció a ViewGroup számára, amely nem rajzol saját tartalmat, csak gyermekelemeket
  • A dispatchDraw felülírását effektek (blur, overlay) vagy a gyermekelemek rajzolásának animálására használják

Mi az a dispatchDraw()?

dispatchDraw(Canvas canvas) — a ViewGroup osztály protected metódusa, amelyet az Android rendszer hív meg az aktuális konténer összes gyermek View-jának rajzolásához. A dispatchDraw a szabványos draw csővezeték része: először az onDraw hajtódik végre (a View saját tartalmának rajzolása), majd a dispatchDraw (a gyermekek rekurzív rajzolása), majd az onDrawForeground (a foreground és scrollbar-ok rajzolása). A metódus nem alkalmas közvetlen meghívásra az alkalmazás kódjából.

A dispatchDraw szabványos implementációja a ViewGroup-ban végigiterál az összes gyermek View-n, ellenőrzi azok láthatóságát, és mindegyikhez meghívja a draw(Canvas) metódust. A bejárás sorrendje megfelel a gyermekek indexeinek (0-tól childCount - 1-ig). Ha a gyermek View-k átfedik egymást, a listában utolsók az elsők fölé rajzolódnak. A sorrend megváltoztatásával a dispatchDraw-ban módosítható a Z-order rajzolási sorrend.

View (nem ViewGroup) esetén a dispatchDraw metódus üres — egy szokásos View-nak nincsenek gyermekelemei, így nincs mit rajzolni. A metódus létezik a View alaposztályban, de csak ViewGroup példányoknál hívódik meg. A fejlesztő ellenőrizheti, hogy a View-nak vannak-e gyermekelemei a dispatchDraw alapértelmezett viselkedésén keresztül, de gyakran egyszerűbb az instanceof ViewGroup ellenőrzés.

Rajzolási csővezeték: onDraw, dispatchDraw, onDrawForeground

A draw(Canvas) metódus a View osztályban háromfázisú rajzolási csővezetéket szervez. Az első fázis — az onDraw(canvas) meghívása, amelyben a View a saját tartalmát rajzolja. A második fázis — a dispatchDraw(canvas) meghívása, amely elindítja a gyermek View-k rajzolását. A harmadik fázis — az onDrawForeground(canvas), amely a foreground rétegért, scrollbar-okért és ripple effektekért felelős. Ez a sorrend garantálja a grafikus rétegek helyes egymásra helyezését.

onDraw minden a dispatchDraw előtt hajtódik végre. Ez azt jelenti, hogy a szülő View tartalma a gyermek View-k alatt jelenik meg. Ha valamit a gyermekek fölé kell rajzolni, ez az onDrawForeground-ban vagy egy felülírt dispatchDraw-ban történik a super.dispatchDraw(canvas) meghívásával és az azt követő rárajzolással. A háttér (background) még az onDraw előtt kerül kirajzolásra — a drawBackground(canvas) metódusban a draw()-n belül.

Hardveres gyorsításnál (hardware acceleration) a draw csővezeték a GPU-n keresztül működik. A View egy DisplayList-ben rajzolódik ki — egy rajzolási parancslistában, amely gyorsítótárazásra kerül és újra felhasználható. A dispatchDraw hardveresen gyorsított módban hozzáadja a gyermek View-k DisplayList-jét a jelenet általános DisplayList-jéhez. A dispatchDraw megváltoztatása a DisplayList érvénytelenítéséhez és a gyorsítótár újraépítéséhez vezethet, ami befolyásolja a teljesítményt.

MetódusMeghívás sorrendjeRendeltetés
drawBackground1Háttér rajzolása (background drawable)
onDraw2A View saját tartalmának rajzolása
dispatchDraw3Az összes gyermek View rajzolása (csak ViewGroup számára)
onDrawForeground4Foreground, scrollbar és ripple effekt rajzolása

Miben különbözik a dispatchDraw az onDraw-tól

onDraw felelős a View saját tartalmának — alakzatok, szöveg, képek — rajzolásáért, amelyek ehhez a konkrét elemhez tartoznak. dispatchDraw felelős a gyermek View-k — a ViewGroup-on belüli összes elem — rajzolásáért. Ha a ViewGroup nem írja felül a dispatchDraw-ot, a ViewGroup-ból származó implementáció kerül használatra, amely egyszerűen rekurzívan végigmegy a childCount-on és meghívja a draw metódust minden gyermekhez.

Olyan ViewGroup esetén, amely nem rajzol saját tartalmat (pl. FrameLayout, LinearLayout), az onDraw optimalizálható a setWillNotDraw(true) beállításával. Ebben az esetben az onDraw soha nem hívódik meg, ami erőforrásokat takarít meg. A dispatchDraw eközben továbbra is működik és meghívja a draw metódust a gyermekelemekhez. Az összes szabványos ViewGroup (LinearLayout, RelativeLayout, ConstraintLayout) használja a setWillNotDraw(true) beállítást.

Ha a dispatchDraw felülírásra kerül a super.dispatchDraw(canvas) meghívása nélkül, a gyermek View-k nem rajzolódnak ki. Ez hasznos lehet az összes gyermek ideiglenes elrejtéséhez, de a legtöbb esetben hibákat okoz. Ajánlott gyakorlat: hívja meg a super.dispatchDraw(canvas) metódust a felülírt metódus elején, majd adja hozzá saját grafikáját (pl. overlay alfa csatornával) a gyermekek fölé.

Példa a dispatchDraw felülírására Kotlinban

Hozzunk létre egy OverlayViewGroup -t — egy egyedi ViewGroup-ot, amely egy félig átlátszó réteget ad szöveges címkével az összes gyermek View fölé. A dispatchDraw először meghívja a super metódust (az összes gyermek rajzolása), majd kirajzolja az overlay téglalapot és a szöveget. A Paint a konstruktorban jön létre a draw ciklusbeli allokációk elkerülése érdekében.

kotlin
class OverlayViewGroup(context: Context)
    : ViewGroup(context) {

    private val overlayPaint = Paint().apply {
        color = Color.parseColor("#66000000")
        style = Paint.Style.FILL
    }

    private val textPaint = Paint().apply {
        color = Color.WHITE
        textSize = 36f
        isAntiAlias = true
        textAlign = Paint.Align.CENTER
    }

    init {
        setWillNotDraw(false)
    }

    override fun dispatchDraw(canvas: Canvas) {
        super.dispatchDraw(canvas)
        canvas.drawRect(0f, 0f,
                        width.toFloat(),
                        height.toFloat(), overlayPaint)
        canvas.drawText("ELŐNÉZETI MÓD",
            width / 2f,
            height / 2f, textPaint)
    }

    override fun onLayout(changed: Boolean,
                       l: Int, t: Int,
                       r: Int, b: Int) {
        var top = t
        for (i in 0 until childCount) {
            val child = getChildAt(i)
            val cw = child.measuredWidth
            val ch = child.measuredHeight
            child.layout(l, top, l + cw, top + ch)
            top += ch
        }
    }

    override fun onMeasure(widthMeasureSpec: Int,
                       heightMeasureSpec: Int) {
        measureChildren(widthMeasureSpec, heightMeasureSpec)
        val maxWidth = resolveSize(
            getChildAt(0).measuredWidth,
            widthMeasureSpec)
        var totalHeight = 0
        for (i in 0 until childCount) {
            totalHeight += getChildAt(i).measuredHeight
        }
        setMeasuredDimension(maxWidth,
            resolveSize(totalHeight, heightMeasureSpec))
    }
}

Animált Z-order dispatchDraw segítségével

A következő példa a gyermekelemek rajzolási sorrendjének megváltoztatását mutatja be. A CircularRevealLayout felülírja a dispatchDraw-ot és fordított sorrendben rajzolja a gyermekeket, fordított Z-order effektet létrehozva. Az animációhoz egy ciklikus eltolás kerül hozzáadásra a rajzolási indexhez az animált offset érték alapján.

kotlin
class ReverseOrderLayout(context: Context)
    : ViewGroup(context) {

    private var reverse = false

    override fun dispatchDraw(canvas: Canvas) {
        if (!reverse) {
            super.dispatchDraw(canvas)
            return
        }
        for (i in childCount - 1 downTo 0) {
            val child = getChildAt(i)
            if (child.visibility == GONE) continue
            drawChild(canvas, child, getDrawingTime())
        }
    }

    fun toggleReverse() {
        reverse = !reverse
        invalidate()
    }

    override fun onLayout(changed: Boolean,
                       l: Int, t: Int,
                       r: Int, b: Int) {
        var left = l
        for (i in 0 until childCount) {
            val child = getChildAt(i)
            val cw = child.measuredWidth
            child.layout(left, t,
                left + cw, t + child.measuredHeight)
            left += cw
        }
    }

    override fun onMeasure(widthMeasureSpec: Int,
                       heightMeasureSpec: Int) {
        measureChildren(widthMeasureSpec, heightMeasureSpec)
        var totalWidth = 0
        for (i in 0 until childCount) {
            totalWidth += getChildAt(i).measuredWidth
        }
        setMeasuredDimension(
            resolveSize(totalWidth, widthMeasureSpec),
            resolveSize(getChildAt(0).measuredHeight,
                heightMeasureSpec))
    }
}

Mikor használjuk a dispatchDraw-ot Androidban

dispatchDraw olyan forgatókönyvekben használatos, ahol befolyásolni kell a gyermek View-k rajzolási folyamatát anélkül, hogy a ViewGroup saját tartalmát érintené. Fő forgatókönyvek: közös effekt (overlay, shadow, gradient) alkalmazása az összes gyermek fölé; a Z-order sorrend megváltoztatása mélységhatás létrehozásához; a gyermekelemek megjelenésének vagy eltűnésének animálása Canvas transzformációkon keresztül.

Ha a grafika a gyermekelemek alá kerül (háttér effekt), az onDraw-t kell használni — a dispatchDraw utána hívódik. Ha a grafikának a gyermekek fölött kell lennie — használja a dispatchDraw-ot super hívással, majd a Canvas.draw-t. Ha globális szín- vagy átlátszóságváltoztatás szükséges, kényelmes a canvas.saveLayerAlpha() használata a dispatchDraw-ban, amely körülöleli a gyermekek rajzolását.

Nem ajánlott a dispatchDraw használata a következőkre: 1) összetett animáció rajzolása valós időben (használja az invalidate és onDraw metódusokat); 2) ViewGroup képernyőképek készítése (használja a buildDrawingCache() vagy View.draw(Canvas) metódusokat); 3) gyermek View-k módosítása (koordináták, méretek — ez az onLayout feladata, nem a dispatchDraw-é). A dispatchDraw — csak vizuális effektek alkalmazására.

Gyakran Ismételt Kérdések

Meghívható-e a dispatchDraw közvetlenül rendszer nélkül?

Nem, a dispatchDraw — protected metódus, amelyet az Android rendszer hív meg a nyilvános draw() metóduson belül. A dispatchDraw közvetlen meghívásának nincs értelme, mivel nem végez előkészítő műveleteket (Canvas mentése, háttér és foreground rajzolása). Ehelyett használja a View.draw(Canvas) metódust programozott rajzoláshoz tetszőleges Canvas-ba.

Mi történik a super.dispatchDraw meghívásakor egyedi rajzolás után?

Ha a super.dispatchDraw(canvas) metódust a saját rajzolási műveletei után hívja, a gyermek View-k az egyedi grafika fölé rajzolódnak. Ez megváltoztatja a rétegek egymásra helyezését: először az egyedi réteg rajzolódik, majd a gyermekek. Ha fordított helyzetre van szükség (grafika a gyermekek fölött), először hívja a super.dispatchDraw metódust, majd a saját műveleteit.

Hogyan befolyásolja a dispatchDraw a teljesítményt?

dispatchDraw meghívja a draw() metódust minden gyermek View-hoz, a teljes idő arányos a gyermekek számával. Hardveres gyorsításnál a Canvas műveletek hozzáadása a dispatchDraw-hoz a DisplayList érvénytelenítéséhez és a gyorsítótár újraépítéséhez vezethet. 5–10 gyermek esetén a hatás minimális, 50+ gyermek esetén az összetett overlay-ek gyorsítótárazása ajánlott.

Miért nem hívódik meg a dispatchDraw View esetén?

View (nem ViewGroup) nem rendelkezik gyermek elemekkel, ezért a dispatchDraw nem végez hasznos munkát. A metódus azonban létezik a View alaposztályban a polimorfizmus miatt: a draw()-ban lévő kód meghívja a dispatchDraw-ot bármely View-hoz, de a View implementációja nem tartalmaz logikát a gyermekek rajzolásához. Csak a ViewGroup írja felül a dispatchDraw-ot.

Megváltoztatható-e a Canvas a dispatchDraw-ban?

Igen, a dispatchDraw paraméterül kap egy Canvas-t, és az transzformálható a super.dispatchDraw meghívása előtt (translate, rotate, scale). Ez az összes gyermek együttes egészként történő animálására alkalmazható. A super.dispatchDraw befejezése után javasolt a Canvas visszaállítása az eredeti állapotba.

Összefoglalás

  • dispatchDraw(Canvas) — ViewGroup metódus az összes gyermek View rekurzív rajzolásához az onDraw után
  • Draw csővezeték: drawBackground → onDraw → dispatchDraw → onDrawForeground
  • onDraw a View saját tartalmát rajzolja, dispatchDraw — a gyermek elemek tartalmát
  • setWillNotDraw(true) optimalizálja a ViewGroup-ot, kihagyja az onDraw-t, de a dispatchDraw továbbra is működik
  • A dispatchDraw felülírását effektek alkalmazására, Z-order módosítására és a gyermekek Canvas transzformációjára használják
  • A super.dispatchDraw és az egyedi rajzolás sorrendje határozza meg, mi kerül a gyermekek fölé
  • Szokásos View (nem ViewGroup) esetén a dispatchDraw üres — nincsenek gyermek elemei

Kulcsrakész mobilalkalmazást fejlesztünk

Az IT Sectr 2017 óta készít iOS és Android alkalmazásokat induló vállalkozásoknak és vállalkozásoknak. Tanácsot adunk, és a legjobb megoldást javasoljuk.

Projekt megbeszélése

Olvassa el is