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 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.
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ódus | Meghívás sorrendje | Rendeltetés |
|---|---|---|
| drawBackground | 1 | Háttér rajzolása (background drawable) |
| onDraw | 2 | A View saját tartalmának rajzolása |
| dispatchDraw | 3 | Az összes gyermek View rajzolása (csak ViewGroup számára) |
| onDrawForeground | 4 | Foreground, scrollbar és ripple effekt rajzolása |
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é.
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.
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))
}
}
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.
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))
}
}
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
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.
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.
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.
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.
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
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.
Olvassa el is