UI Design
Layout Rendering
Graphic Design
Frontend Development
Digital Design

How can you tell when a layout has been drawn?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

In UI work, "drawn" can mean different things depending on the platform: measured, laid out, attached, or actually rendered on screen. If you need reliable code that runs after a view has a real size, you usually want a layout callback rather than guessing with delays or arbitrary timers.

Understand the Difference Between Layout and Draw

A view often goes through several phases:

  • It is attached to the hierarchy
  • It is measured
  • It is laid out with width and height
  • It is drawn to the screen

Many bugs come from mixing these phases up. If your code needs final width and height, you usually care about the layout pass, not necessarily the exact pixel draw event.

A Reliable Android Approach: doOnLayout

On Android, a simple modern option is doOnLayout, which runs after the view has been laid out.

kotlin
1import android.os.Bundle
2import android.widget.TextView
3import androidx.appcompat.app.AppCompatActivity
4import androidx.core.view.doOnLayout
5
6class MainActivity : AppCompatActivity() {
7    override fun onCreate(savedInstanceState: Bundle?) {
8        super.onCreate(savedInstanceState)
9        val textView = TextView(this)
10        textView.text = "Hello"
11        setContentView(textView)
12
13        textView.doOnLayout {
14            println("Width: ${it.width}, height: ${it.height}")
15        }
16    }
17}

This is a good default when you want to position another element, start an animation based on size, or read the final bounds.

Use ViewTreeObserver for Broader Hooks

If you need a lower-level callback, ViewTreeObserver gives you access to layout and pre-draw events.

kotlin
1val view = findViewById<TextView>(android.R.id.content)
2
3view.viewTreeObserver.addOnGlobalLayoutListener(
4    object : android.view.ViewTreeObserver.OnGlobalLayoutListener {
5        override fun onGlobalLayout() {
6            println("Layout complete: ${view.width} x ${view.height}")
7            view.viewTreeObserver.removeOnGlobalLayoutListener(this)
8        }
9    }
10)

OnGlobalLayoutListener fires whenever the global layout state changes, so you usually remove it after the first useful callback unless you want repeated notifications.

If you need an even later moment, OnPreDrawListener is closer to the actual draw step.

On the Web: Wait for Layout-Dependent Information Properly

In web development, layout is typically available after the browser has rendered the DOM and styles. A common pattern is to inspect the element inside requestAnimationFrame.

html
1<div id="box" style="width: 200px; height: 80px; background: salmon;"></div>
2<script>
3  const box = document.getElementById("box");
4
5  requestAnimationFrame(() => {
6    const rect = box.getBoundingClientRect();
7    console.log(rect.width, rect.height);
8  });
9</script>

This is often more reliable than reading layout immediately after DOM mutation, especially if styles or fonts are still settling.

Avoid Guessing with Delays

A bad but common pattern is:

kotlin
view.postDelayed({
    println(view.width)
}, 100)

This sometimes appears to work, but it is timing-based rather than lifecycle-based. Fast devices, slow devices, complex layouts, and animations can all make the assumption wrong.

Prefer callbacks tied to the actual layout system instead of arbitrary delays.

Common Pitfalls

The biggest mistake is assuming onCreate, constructor code, or immediate DOM insertion means the layout already has final dimensions. At that point, width and height are often still zero or provisional.

Another issue is confusing "laid out" with "drawn." If you need screen pixels for screenshots or rendering synchronization, a layout callback may still be too early and a pre-draw or frame callback may be more appropriate.

Developers also often forget to remove global listeners. That can cause repeated callbacks, duplicate work, or leaks if the listener outlives the view's useful lifecycle.

Finally, avoid sleep calls and arbitrary timers. They make UI code flaky because they depend on performance characteristics rather than on the framework lifecycle.

Summary

  • Use lifecycle-aware callbacks instead of timing guesses to know when layout work is finished.
  • On Android, doOnLayout is a simple way to run code after final view sizing.
  • 'ViewTreeObserver provides lower-level layout and pre-draw hooks when needed.'
  • On the web, requestAnimationFrame is a common way to inspect post-layout geometry.
  • Decide whether you need layout completion or actual drawing, because they are not the same event.

Course illustration
Course illustration

All Rights Reserved.