dispatchDraw() — je metoda třídy ViewGroup odpovědná za rekurzivní kreslení všech podřízených View v hierarchii Android. Podle Android Developers Documentation (2026) je dispatchDraw automaticky volán po onDraw v metodě draw() hlavního View a prochází všechny podřízené prvky a volá jejich vlastní draw(). Vývojáři přepisují dispatchDraw ve vlastních ViewGroup pro přidávání efektů, vkládání grafiky přes děti nebo změnu pořadí kreslení.
Hlavní
dispatchDraw(Canvas canvas) — je protected metoda třídy ViewGroup, kterou systém Android volá pro kreslení všech podřízených View aktuálního kontejneru. dispatchDraw je součástí standardního potrubí draw: nejprve se provede onDraw (kreslení obsahu samotného View), poté dispatchDraw (rekurzivní kreslení dětí), poté onDrawForeground (kreslení foreground a scrollbarů). Metoda není určena k přímému volání z kódu aplikace.
Standardní implementace dispatchDraw ve ViewGroup iteruje všemi podřízenými View, kontroluje jejich viditelnost a volá pro každé draw(Canvas). Pořadí průchodu odpovídá indexům dětí (od 0 do childCount - 1). Pokud se podřízené View překrývají, poslední v seznamu se kreslí přes první. Změnou pořadí v dispatchDraw lze změnit Z-order kreslení.
Pro View (ne ViewGroup) je metoda dispatchDraw prázdná — běžné View nemá podřízené prvky, takže není co kreslit. Metoda existuje v základní třídě View, ale je volána pouze u instancí ViewGroup. Vývojář může zkontrolovat, zda má View podřízené prvky, pomocí výchozího chování dispatchDraw, ale často je jednodušší zkontrolovat instanceof ViewGroup.
Metoda draw(Canvas) třídy View organizuje třífázové potrubí kreslení. První fáze — volání onDraw(canvas), ve kterém View kreslí svůj obsah. Druhá fáze — volání dispatchDraw(canvas), které spouští kreslení podřízených View. Třetí fáze — onDrawForeground(canvas), odpovědný za vrstvu foreground, scrollbary a efekty ripple. Tato sekvence zaručuje správné pořadí překrývání grafických vrstev.
onDraw se vždy provádí před dispatchDraw. To znamená, že obsah nadřazeného View se zobrazuje pod podřízenými View. Pokud je třeba nakreslit něco přes děti, děje se tak v onDrawForeground nebo v přepsaném dispatchDraw s voláním super.dispatchDraw(canvas) a následným kreslením přes. Pozadí (background) se kreslí ještě před onDraw — v drawBackground(canvas) uvnitř metody draw().
Při hardwarové akceleraci (hardware acceleration) pracuje potrubí draw přes GPU. View se kreslí do DisplayList — seznamu příkazů kreslení, který je ukládán do mezipaměti a znovu používán. dispatchDraw v režimu hardwarové akcelerace přidává DisplayList podřízených View do obecného DisplayList scény. Změna dispatchDraw může vést k zneplatnění DisplayList a přestavbě mezipaměti, což ovlivňuje výkon.
| Metoda | Pořadí volání | Účel |
|---|---|---|
| drawBackground | 1 | Kreslení pozadí (background drawable) |
| onDraw | 2 | Kreslení obsahu samotného View |
| dispatchDraw | 3 | Kreslení všech podřízených View (pouze pro ViewGroup) |
| onDrawForeground | 4 | Kreslení foreground, scrollbar a ripple efektu |
onDraw je zodpovědný za kreslení obsahu samotného View — tvarů, textu, obrázků, které patří k tomuto konkrétnímu prvku. dispatchDraw je zodpovědný za kreslení podřízených View — všech prvků, které se nacházejí uvnitř ViewGroup. Pokud ViewGroup nepřepisuje dispatchDraw, použije se implementace z ViewGroup, která jednoduše rekurzivně prochází childCount a volá draw pro každé dítě.
Pro ViewGroup, která nekreslí vlastní obsah (např. FrameLayout, LinearLayout), lze onDraw optimalizovat nastavením setWillNotDraw(true). V tomto případě není onDraw nikdy volán, což šetří prostředky. dispatchDraw přitom nadále funguje a volá draw pro podřízené prvky. Všechny standardní ViewGroup (LinearLayout, RelativeLayout, ConstraintLayout) používají setWillNotDraw(true).
Pokud je dispatchDraw přepsán bez volání super.dispatchDraw(canvas), podřízené View nebudou nakresleny. To může být užitečné pro dočasné skrytí všech dětí, ale ve většině případů způsobuje chyby. Doporučená praxe: volejte super.dispatchDraw(canvas) na začátku přepsané metody a poté přidejte vlastní grafiku (např. overlay s alfa kanálem) přes děti.
Vytvořme OverlayViewGroup — vlastní ViewGroup, která přidává poloprůhlednou vrstvu s textovým štítkem přes všechny podřízené View. dispatchDraw nejprve volá super (kreslení všech dětí), poté kreslí obdélník overlay a text. Paint je vytvořen v konstruktoru, aby se zabránilo alokacím ve smyčce draw.
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("REŽIM NÁHLEDU",
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))
}
}
Následující příklad ukazuje změnu pořadí kreslení podřízených prvků. CircularRevealLayout přepisuje dispatchDraw a kreslí děti v obráceném pořadí, čímž vytváří efekt obráceného Z-order. Pro animaci je přidán cyklický posun indexu kreslení na základě animované hodnoty offset.
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 se používá ve scénářích, kde je potřeba ovlivnit proces kreslení podřízených View, aniž by byl ovlivněn vlastní obsah ViewGroup. Hlavní scénáře: aplikace společného efektu (overlay, shadow, gradient) přes všechny děti; změna pořadí Z-order pro vytvoření efektu hloubky; animace objevování nebo mizení podřízených prvků pomocí Canvas transformací.
Pokud je vyžadováno vložení grafiky pod podřízené prvky (efekt pozadí), použijte onDraw — dispatchDraw je volán po něm. Pokud má být grafika přes děti — použijte dispatchDraw s voláním super, poté Canvas.draw. Pokud je vyžadována globální změna barvy nebo průhlednosti, je vhodné použít canvas.saveLayerAlpha() v dispatchDraw, obalující kreslení dětí.
Nedoporučuje se používat dispatchDraw pro: 1) kreslení složité animace v reálném čase (použijte invalidate a onDraw); 2) vytváření snímků obrazovky ViewGroup (použijte buildDrawingCache() nebo View.draw(Canvas)); 3) úpravu podřízených View (souřadnice, velikosti — to je úkol onLayout, ne dispatchDraw). dispatchDraw — pouze pro aplikaci vizuálních efektů.
Často kladené otázky
Ne, dispatchDraw — protected metoda volaná systémem Android uvnitř veřejné metody draw(). Přímé volání dispatchDraw nemá smysl, protože neprovádí přípravné akce (uložení Canvas, kreslení pozadí a foreground). Místo toho použijte View.draw(Canvas) pro programové kreslení do libovolného Canvas.
Pokud zavoláte super.dispatchDraw(canvas) po vlastních operacích kreslení, podřízené View budou nakresleny přes vlastní grafiku. Tím se změní překrývání vrstev: nejprve se kreslí vlastní vrstva, poté děti. Pokud je potřeba opačná situace (grafika přes děti), nejprve zavolejte super.dispatchDraw a poté vlastní operace.
dispatchDraw volá draw() pro každé podřízené View, celkový čas je úměrný počtu dětí. Při hardwarové akceleraci může přidání Canvas operací v dispatchDraw vést k zneplatnění DisplayList a přestavbě mezipaměti. Pro 5–10 dětí je dopad minimální, pro 50+ dětí se doporučuje ukládání složitých překryvů do mezipaměti.
View (ne ViewGroup) nemá podřízené prvky, proto dispatchDraw nevykonává užitečnou práci. Metoda však existuje v základní třídě View kvůli polymorfismu: kód v draw() volá dispatchDraw pro jakékoli View, ale implementace View neobsahuje logiku kreslení dětí. Pouze ViewGroup přepisuje dispatchDraw.
Ano, dispatchDraw přijímá Canvas a lze jej transformovat před voláním super.dispatchDraw (translate, rotate, scale). To se používá pro animaci celé sady dětí jako jednoho celku. Po dokončení super.dispatchDraw se doporučuje obnovit Canvas do původního stavu.
Shrnutí
Vyvineme mobilní aplikaci na klíč
IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.
Přečtěte si také