onMeasure(): what it is, MeasureSpec modes and method overriding

Author: IT Sectr Published: 2026-07-22 Reading time: 9 min

onMeasure() is a protected method of the android.view.View class that is called by the Android system to determine the size of a View. The system passes two MeasureSpec objects to the method, each containing a measurement mode (EXACTLY, AT_MOST, or UNSPECIFIED) and a size suggested by the parent container. According to the Android Developers Documentation (2026), overriding onMeasure with correct MeasureSpec handling is a mandatory step for all custom Views and ViewGroups that require precise size control.

Key Takeaways

  • onMeasure(int widthMeasureSpec, int heightMeasureSpec) — the View method for measuring sizes with MeasureSpec passed from the parent
  • MeasureSpec — a 32-bit value encoding the measurement mode (UNSPECIFIED, EXACTLY, AT_MOST) and size
  • setMeasuredDimension(int w, int h) — a mandatory call inside onMeasure that sets the final View dimensions
  • Two-pass algorithm for measurement: the parent measures children, then children report their sizes, and the parent makes the final decision
  • measureChildWithMargins — a helper method for measuring child Views in custom ViewGroups

What is onMeasure()?

onMeasure(int widthMeasureSpec, int heightMeasureSpec) is a method of the View class that the Android system calls to determine the width and height of a view. The developer overrides this method to specify what size the View should be based on the constraints passed in MeasureSpec. Without correct onMeasure overriding, a custom View may display incorrectly or not appear at all.

The system calls onMeasure during the measure phase of the View lifecycle, which precedes the layout (onLayout) and draw (onDraw) phases. If a View does not override onMeasure, the superclass implementation is used, which sets default sizes based on background drawable or layout_params. Calling super.onMeasure(widthMeasureSpec, heightMeasureSpec) only works for standard View subclasses such as TextView or ImageView.

A key requirement for onMeasure is that the call to setMeasuredDimension(int, int) must be present at the end of the method. If this call is missing, the system throws an IllegalStateException stating that the View did not set measured dimensions. The final dimensions become available through the getMeasuredWidth() and getMeasuredHeight() getters after the measure phase completes.

MeasureSpec Modes: Three Key Values

MeasureSpec is a 32-bit integer where the top 2 bits encode the measurement mode and the lower 30 bits encode the size. The mode determines how free the View is in choosing its own size. Android provides three modes: EXACTLY, AT_MOST, and UNSPECIFIED. Each mode dictates different processing logic in onMeasure.

MeasureSpec ModeValueBehavior
EXACTLYParent specified an exact sizeThe View must fit exactly into the given size if it does not want to exceed boundaries
AT_MOSTParent set a maximum sizeThe View can choose any size from 0 to the given maximum
UNSPECIFIEDParent imposes no constraintsThe View can choose any desired size with no upper limit

To extract the mode and size from MeasureSpec, static methods of the MeasureSpec class are used: MeasureSpec.getMode(int) returns one of the three modes (EXACTLY, AT_MOST, UNSPECIFIED), and MeasureSpec.getSize(int) returns the numeric size in pixels. To create a custom MeasureSpec, use MeasureSpec.makeMeasureSpec(int size, int mode). These three methods cover all scenarios for working with sizes in onMeasure.

Typical MeasureSpec Handling Logic

The standard pattern for handling MeasureSpec: if the mode is EXACTLY, use the given size as final; if AT_MOST, choose the minimum of the desired size (the View's content) and the given maximum; if UNSPECIFIED, use the desired View size without constraints. This pattern ensures correct behavior under any parent constraints.

kotlin
override fun onMeasure(widthMeasureSpec: Int,
                       heightMeasureSpec: Int) {
    val desiredWidth = 200
    val desiredHeight = 100

    val widthMode = MeasureSpec.getMode(widthMeasureSpec)
    val widthSize = MeasureSpec.getSize(widthMeasureSpec)
    val heightMode = MeasureSpec.getMode(heightMeasureSpec)
    val heightSize = MeasureSpec.getSize(heightMeasureSpec)

    val width = when (widthMode) {
        MeasureSpec.EXACTLY -> widthSize
        MeasureSpec.AT_MOST -> minOf(desiredWidth, widthSize)
        else -> desiredWidth
    }
    val height = when (heightMode) {
        MeasureSpec.EXACTLY -> heightSize
        MeasureSpec.AT_MOST -> minOf(desiredHeight, heightSize)
        else -> desiredHeight
    }
    setMeasuredDimension(width, height)
}

Using resolveSize

To simplify the standard logic, Android provides the resolveSizeAndState method, which takes the desired size, MeasureSpec, and returns the final size with the correct mode. This method implements the pattern described above in a single line of code. The resolveSize(int size, int measureSpec) function is also available, returning a clean size without state bits.

Two-Pass Measurement Algorithm

Android uses a two-pass measurement algorithm that ensures every View in the hierarchy gets correct dimensions considering parent constraints and child preferences. On the first pass, the parent passes MeasureSpec with constraints to child Views, and child Views compute their desired sizes. On the second pass, the parent makes the final size decision.

For ViewGroup, the measurement process is more complex: the parent must first measure all its children, then determine its own size based on their sizes. Calling measureChildren(int widthMeasureSpec, int heightMeasureSpec) iterates through all child Views and calls measure(child, childWidthSpec, childHeightSpec) for each. After measuring all children, the ViewGroup calls setMeasuredDimension with its own dimensions.

An important nuance: the measure method (public, final) cannot be overridden — instead, onMeasure is overridden. This ensures the system can perform housekeeping tasks before and after onMeasure, such as checking size changes and calculating the dirty area for subsequent drawing. If a View has fixed dimensions, overriding onMeasure may not be necessary.

MEASURED_SIZE_STATE Flag

MeasureSpec includes not only the size and mode but also state bits, accessible via MeasureSpec.getMode(). After calling setMeasuredDimension, the state becomes part of the View's measured dimensions and can be checked via getMeasuredState(). This is used in ScrollView and other scrollable containers for correctly passing constraints to children.

Example of Overriding onMeasure in Kotlin

Let us examine a practical example of creating a custom View with onMeasure overridden for square display. The SquareView class extends View and ensures that width and height are always equal, regardless of the passed MeasureSpec. In onMeasure, the minimum side is determined and the square size is set.

kotlin
class SquareView(context: Context)
    : View(context) {

    override fun onMeasure(widthMeasureSpec: Int,
                       heightMeasureSpec: Int) {
        val widthSize =
            MeasureSpec.getSize(widthMeasureSpec)
        val heightSize =
            MeasureSpec.getSize(heightMeasureSpec)
        val size = minOf(widthSize, heightSize)
        setMeasuredDimension(size, size)
    }
}

Custom ViewGroup with Child Measurement

ViewGroup requires more complex onMeasure logic because children must be measured first, then the ViewGroup's own size is determined. The CascadeLayout example distributes children in a cascade with an offset. After measuring all children via measureChildWithMargins, the total width and height are calculated.

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

    private val cascadeOffset = 40

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

    override fun generateLayoutParams(attrs: AttributeSet?)
        : LayoutParams = MarginLayoutParams(context, attrs)

    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 left = l + cascadeOffset * i
            child.layout(left, top,
                left + child.measuredWidth,
                top + child.measuredHeight)
            top += child.measuredHeight
        }
    }
}

Common Mistakes When Overriding onMeasure

Missing setMeasuredDimension call is the most common mistake. If a developer overrides onMeasure but does not call setMeasuredDimension, the application crashes with IllegalStateException. This is especially common when the method has conditional branches and one branch lacks the call. Every code branch in onMeasure must end with a call to setMeasuredDimension.

Ignoring AT_MOST mode is the second most frequent mistake. If a View in AT_MOST mode always uses the passed size instead of calculating based on content, the parent container cannot properly distribute space. For example, a TextView in AT_MOST should calculate the text width and use the minimum of the desired and passed widths. Ignoring AT_MOST causes the View to occupy all available space even with small content.

Creating objects inside onMeasure is a classic performance mistake. Since onMeasure can be called multiple times (on every layout request), creating objects (Paint, Rect, String) inside this method clutters memory and triggers garbage collection. All objects should be created once in the View constructor, and only size calculation logic should run in onMeasure. The same rule applies to onDraw and onLayout.

Measuring Child Views in ViewGroup

measureChildWithMargins is a protected ViewGroup method that measures a single child View considering its MarginLayoutParams. The method accepts the parent MeasureSpec and accumulated width and height offsets. It automatically adjusts the MeasureSpec for the child View by subtracting parent paddings and child margins, then passes the adjusted MeasureSpec to child.measure().

For advanced measurement logic, a ViewGroup can override measureChild(View child, int parentWidthSpec, int parentHeightSpec) or work directly with MeasureSpec for each child. For example, LinearLayout in onMeasure iterates through all child Views, measures each considering its layout_weight, and distributes the remaining space proportionally. This approach allows implementing arbitrary layout algorithms.

Caching measurement results via the measure cache mechanism is available through the setMeasureWithLargestChildEnabled flag in certain ViewGroups. However, in most cases onMeasure is called again on any layout change, and caching does not apply. In custom ViewGroups, it is recommended to minimize computations in onMeasure rather than rely on caching.

Frequently Asked Questions

Is it necessary to override onMeasure for a custom View?

Yes, if the custom View inherits directly from the View class. If it inherits from TextView, ImageView, or Button with their standard sizes, onMeasure can be left unchanged. For ViewGroup, overriding onMeasure is always required — otherwise children will not be correctly measured.

What happens if setMeasuredDimension is not called?

The Android system throws IllegalStateException with the message “The View did not call setMeasuredDimension”. This exception occurs in the measure() method after onMeasure completes, if the final dimensions remained zero. The exception crashes the application if not handled via try-catch.

What is the difference between getWidth and getMeasuredWidth?

getMeasuredWidth() returns the size set in onMeasure (measurement phase). getWidth() returns the actual size the View received in onLayout after all positioning adjustments. For most Views these values match, but in custom ViewGroups they may differ.

Can animations or View state be changed inside onMeasure?

No, onMeasure is intended exclusively for size calculation. Changing state, starting animations, networking, or updating data in this method violates Android architecture and may lead to recursive measure calls, since state changes can trigger requestLayout.

How does onMeasure interact with ConstraintLayout?

ConstraintLayout manages child measurement independently based on defined constraints. If a custom View inside ConstraintLayout overrides onMeasure, it must correctly handle the MeasureSpec passed from ConstraintLayout, otherwise constraints may not work. ConstraintLayout uses a two-pass algorithm with its own WidgetContainer for calculation.

Summary

  • onMeasure() — the View method for determining dimensions, called by the Android system during the measure phase
  • MeasureSpec encodes the measurement mode (EXACTLY, AT_MOST, UNSPECIFIED) and size passed from the parent
  • setMeasuredDimension — a mandatory call at the end of onMeasure that sets the final dimensions
  • resolveSize — a helper method implementing standard MeasureSpec handling logic in one line
  • Two-pass algorithm ensures correct measurement in the parent-child hierarchy
  • measureChildWithMargins is used for measuring child Views in custom ViewGroups with margins
  • Creating objects inside onMeasure is strongly discouraged due to the risk of GC and frame drops

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.

Discuss the project

Read also