dispatchDraw() is a method of the ViewGroup class responsible for recursive drawing of all child Views in the Android hierarchy. According to the Android Developers Documentation (2026), dispatchDraw is called automatically after onDraw in the draw() method of the parent View and iterates through all child elements, calling their own draw(). Developers override dispatchDraw in custom ViewGroups to add effects, overlay graphics on top of children, or change the drawing order.
Key Takeaways
dispatchDraw(Canvas canvas) is a protected method of the ViewGroup class that the Android system calls to draw all child Views of the current container. dispatchDraw is part of the standard draw pipeline: first onDraw executes (drawing the View's own content), then dispatchDraw (recursive drawing of children), then onDrawForeground (drawing foreground and scrollbars). The method is not intended for direct calling from application code.
The standard dispatchDraw implementation in ViewGroup iterates through all child Views, checks their visibility, and calls draw(Canvas) for each one. The traversal order corresponds to child indices (from 0 to childCount - 1). If child Views overlap, those later in the list are drawn on top of earlier ones. Changing the order in dispatchDraw allows you to modify the Z-order of drawing.
For a View (not a ViewGroup), the dispatchDraw method is empty — a regular View has no child elements, so there is nothing to draw. The method exists in the base View class but is only called on ViewGroup instances. A developer can check whether a View has child elements through dispatchDraw's default behavior, but it is often simpler to check instanceof ViewGroup.
The draw(Canvas) method of the View class organizes a three-stage drawing pipeline. The first stage is calling onDraw(canvas), where the View draws its own content. The second stage is calling dispatchDraw(canvas), which triggers drawing of child Views. The third stage is onDrawForeground(canvas), responsible for the foreground layer, scrollbars, and ripple effects. This sequence ensures the correct layering order of graphical layers.
onDraw always executes before dispatchDraw. This means the parent View's content is displayed underneath child Views. If you need to draw something on top of children, this is done in onDrawForeground or in an overridden dispatchDraw by calling super.dispatchDraw(canvas) and then drawing on top. The background is drawn even before onDraw — in drawBackground(canvas) inside the draw() method.
With hardware acceleration, the draw pipeline works through the GPU. The View is drawn into a DisplayList — a list of drawing commands that is cached and reused. dispatchDraw in hardware-accelerated mode adds the child Views' DisplayLists to the scene's overall DisplayList. Modifying dispatchDraw may invalidate the DisplayList and cause cache rebuilding, which impacts performance.
| Method | Call Order | Purpose |
|---|---|---|
| drawBackground | 1 | Drawing the background (background drawable) |
| onDraw | 2 | Drawing the View's own content |
| dispatchDraw | 3 | Drawing all child Views (ViewGroup only) |
| onDrawForeground | 4 | Drawing foreground, scrollbar, and ripple effect |
onDraw is responsible for drawing the View's own content — shapes, text, images that belong to this specific element. dispatchDraw is responsible for drawing child Views — all elements contained inside the ViewGroup. If a ViewGroup does not override dispatchDraw, the implementation from ViewGroup is used, which simply recursively iterates through childCount and calls draw for each child.
For a ViewGroup that does not draw its own content (e.g., FrameLayout, LinearLayout), onDraw can be optimized by setting setWillNotDraw(true). In this case, onDraw is never called, saving resources. dispatchDraw continues to work and calls draw for child elements. All standard ViewGroups (LinearLayout, RelativeLayout, ConstraintLayout) use setWillNotDraw(true).
If you override dispatchDraw without calling super.dispatchDraw(canvas), child Views will not be drawn. This may be useful for temporarily hiding all children, but in most cases it causes errors. Recommended practice: call super.dispatchDraw(canvas) at the beginning of the overridden method, then add your own graphics (e.g., an overlay with alpha channel) on top of children.
Let's create an OverlayViewGroup — a custom ViewGroup that adds a semi-transparent layer with a text label on top of all child Views. dispatchDraw first calls super (drawing all children), then draws the overlay rectangle and text. Paint is created in the constructor to avoid allocations in the draw loop.
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("PREVIEW MODE",
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))
}
}
The following example demonstrates changing the order of child element drawing. CircularRevealLayout overrides dispatchDraw and draws children in reverse order, creating an inverted Z-order effect. For animation, a cyclic shift of the drawing index is added based on the animated offset value.
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 is used in scenarios where you need to influence the drawing process of child Views without affecting the ViewGroup's own content. Main scenarios: applying a common effect (overlay, shadow, gradient) on top of all children; changing the Z-order to create depth effects; animating the appearance or disappearance of child elements through Canvas transformations.
If you need to apply graphics underneath child elements (background effect), use onDraw — dispatchDraw is called after it. If graphics should be on top of children — use dispatchDraw with super call first, then Canvas.draw. If you need global color or transparency changes, it is convenient to use canvas.saveLayerAlpha() in dispatchDraw, wrapping the drawing of children.
Not recommended to use dispatchDraw for: 1) drawing complex real-time animation (use invalidate and onDraw); 2) creating screenshots of a ViewGroup (use buildDrawingCache() or View.draw(Canvas)); 3) modifying child Views (coordinates, sizes — these are onLayout tasks, not dispatchDraw). dispatchDraw is only for applying visual effects.
Frequently Asked Questions
No, dispatchDraw is a protected method called by the Android system inside the public draw() method. A direct call to dispatchDraw is meaningless because it does not perform preparatory actions (saving Canvas, drawing background and foreground). Instead, use View.draw(Canvas) for programmatic drawing onto an arbitrary Canvas.
If you call super.dispatchDraw(canvas) after your own drawing operations, child Views will be drawn on top of the custom graphics. This changes layer ordering: the custom layer is drawn first, then children. If you need the opposite situation (graphics on top of children), call super.dispatchDraw first, then your own operations.
dispatchDraw calls draw() for each child View, and the total time is proportional to the number of children. With hardware acceleration, adding Canvas operations in dispatchDraw may invalidate the DisplayList and cause cache rebuilding. For 5–10 children the impact is minimal, for 50+ children it is recommended to cache complex overlays.
A View (not a ViewGroup) has no child elements, so dispatchDraw does not perform any useful work. However, the method exists in the base View class for polymorphism: the code in draw() calls dispatchDraw for any View, but the View implementation does not contain child drawing logic. Only ViewGroup overrides dispatchDraw.
Yes, dispatchDraw accepts a Canvas, and it can be transformed before calling super.dispatchDraw (translate, rotate, scale). This is used for animating the entire set of children as a single unit. After super.dispatchDraw completes, it is recommended to restore the Canvas to its original state.
Summary
We will develop a mobile application turnkey
IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.
Read also