onMeasure(): qué es, modos MeasureSpec y sobrescritura del método

Autor: IT Sectr Publicado: 2026-07-22 Tiempo de lectura: 9 min

onMeasure() es un método protegido de la clase android.view.View que el sistema Android llama para determinar el tamaño de una View. El sistema pasa dos objetos MeasureSpec al método, cada uno contiene un modo de medición (EXACTLY, AT_MOST o UNSPECIFIED) y un tamaño sugerido por el contenedor padre. Según la Documentación para Desarrolladores de Android (2026), sobrescribir onMeasure con un manejo correcto de MeasureSpec es un paso obligatorio para todas las Views y ViewGroups personalizadas que requieren un control preciso del tamaño.

Puntos clave

  • onMeasure(int widthMeasureSpec, int heightMeasureSpec) — el método de View para medir tamaños con MeasureSpec pasado desde el padre
  • MeasureSpec — un valor de 32 bits que codifica el modo de medición (UNSPECIFIED, EXACTLY, AT_MOST) y el tamaño
  • setMeasuredDimension(int w, int h) — una llamada obligatoria dentro de onMeasure que fija las dimensiones finales de la View
  • Algoritmo de dos pasos de medición: el padre mide a los hijos, luego los hijos informan sus tamaños y el padre toma la decisión final
  • measureChildWithMargins — un método auxiliar para medir Views hijas en ViewGroups personalizadas

¿Qué es onMeasure()?

onMeasure(int widthMeasureSpec, int heightMeasureSpec) es un método de la clase View que el sistema Android llama para determinar el ancho y alto de una vista. El desarrollador sobrescribe este método para especificar qué tamaño debe tener la View según las restricciones pasadas en MeasureSpec. Sin una sobrescritura correcta de onMeasure, una View personalizada puede mostrarse incorrectamente o no aparecer en absoluto.

El sistema llama a onMeasure durante la fase measure del ciclo de vida de la View, que precede a las fases de layout (onLayout) y draw (onDraw). Si una View no sobrescribe onMeasure, se usa la implementación de la superclase, que establece tamaños predeterminados basados en el background drawable o layout_params. Llamar a super.onMeasure(widthMeasureSpec, heightMeasureSpec) solo funciona para subclases estándar de View, como TextView o ImageView.

Un requisito clave de onMeasure es que la llamada a setMeasuredDimension(int, int) debe estar presente al final del método. Si falta esta llamada, el sistema lanza una IllegalStateException indicando que la View no fijó las dimensiones medidas. Las dimensiones finales están disponibles a través de los getters getMeasuredWidth() y getMeasuredHeight() después de que la fase measure se completa.

Modos MeasureSpec: tres valores clave

MeasureSpec es un entero de 32 bits donde los 2 bits superiores codifican el modo de medición y los 30 bits inferiores codifican el tamaño. El modo determina qué tan libre está la View para elegir su propio tamaño. Android proporciona tres modos: EXACTLY, AT_MOST y UNSPECIFIED. Cada modo dicta una lógica de procesamiento diferente en onMeasure.

Modo MeasureSpecValorComportamiento
EXACTLYEl padre especificó un tamaño exactoLa View debe ajustarse exactamente al tamaño dado si no quiere salirse de los límites
AT_MOSTEl padre estableció un tamaño máximoLa View puede elegir cualquier tamaño de 0 al máximo dado
UNSPECIFIEDEl padre no impone restriccionesLa View puede elegir cualquier tamaño deseado sin límite superior

Para extraer el modo y el tamaño de MeasureSpec se usan los métodos estáticos de la clase MeasureSpec: MeasureSpec.getMode(int) devuelve uno de los tres modos (EXACTLY, AT_MOST, UNSPECIFIED), y MeasureSpec.getSize(int) devuelve el tamaño numérico en píxeles. Para crear un MeasureSpec personalizado se usa MeasureSpec.makeMeasureSpec(int size, int mode). Estos tres métodos cubren todos los escenarios de trabajo con tamaños en onMeasure.

Lógica típica de manejo de MeasureSpec

El patrón estándar para manejar MeasureSpec: si el modo es EXACTLY, usa el tamaño dado como final; si es AT_MOST, elige el mínimo entre el tamaño deseado (el contenido de la View) y el máximo dado; si es UNSPECIFIED, usa el tamaño deseado de la View sin restricciones. Este patrón garantiza un comportamiento correcto bajo cualquier restricción del padre.

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)
}

Uso de resolveSize

Para simplificar la lógica estándar, Android proporciona el método resolveSizeAndState, que toma el tamaño deseado, MeasureSpec y devuelve el tamaño final con el modo correcto. Este método implementa el patrón descrito anteriormente en una sola línea de código. También está disponible la función resolveSize(int size, int measureSpec), que devuelve un tamaño limpio sin bits de estado.

Algoritmo de medición de dos pasos

Android utiliza un algoritmo de medición de dos pasos que garantiza que cada View en la jerarquía reciba dimensiones correctas considerando las restricciones del padre y las preferencias de los hijos. En el primer paso, el padre pasa MeasureSpec con restricciones a las Views hijas, y las Views hijas calculan sus tamaños deseados. En el segundo paso, el padre toma la decisión final sobre los tamaños.

Para ViewGroup, el proceso de medición es más complejo: el padre primero debe medir a todos sus hijos, luego determinar su propio tamaño basado en los tamaños de ellos. Llamar a measureChildren(int widthMeasureSpec, int heightMeasureSpec) itera a través de todas las Views hijas y llama a measure(child, childWidthSpec, childHeightSpec) para cada una. Después de medir a todos los hijos, la ViewGroup llama a setMeasuredDimension con sus propias dimensiones.

Un matiz importante: el método measure (público, final) no se puede sobrescribir — en su lugar se sobrescribe onMeasure. Esto garantiza que el sistema pueda realizar tareas de mantenimiento antes y después de onMeasure, como verificar cambios de tamaño y calcular el área sucia para el dibujo posterior. Si una View tiene dimensiones fijas, puede que no sea necesario sobrescribir onMeasure.

Bandera MEASURED_SIZE_STATE

MeasureSpec incluye no solo el tamaño y el modo, sino también bits de estado, accesibles a través de MeasureSpec.getMode(). Después de llamar a setMeasuredDimension, el estado pasa a formar parte de las dimensiones medidas de la View y se puede verificar mediante getMeasuredState(). Esto se usa en ScrollView y otros contenedores desplazables para pasar correctamente las restricciones a los hijos.

Ejemplo de sobrescritura de onMeasure en Kotlin

Examinemos un ejemplo práctico de creación de una View personalizada con onMeasure sobrescrito para visualización cuadrada. La clase SquareView extiende View y garantiza que el ancho y el alto sean siempre iguales, independientemente del MeasureSpec pasado. En onMeasure se determina el lado mínimo y se establece el tamaño cuadrado.

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)
    }
}

ViewGroup personalizada con medición de hijos

ViewGroup requiere una lógica onMeasure más compleja porque primero se deben medir los hijos, luego se determina el tamaño de la propia ViewGroup. El ejemplo CascadeLayout distribuye los hijos en cascada con un desplazamiento. Después de medir todos los hijos mediante measureChildWithMargins, se calculan el ancho y alto totales.

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
        }
    }
}

Errores comunes al sobrescribir onMeasure

Falta de llamada a setMeasuredDimension es el error más común. Si un desarrollador sobrescribe onMeasure pero no llama a setMeasuredDimension, la aplicación falla con IllegalStateException. Esto ocurre especialmente cuando el método tiene ramas condicionales y en una de ellas falta la llamada. Cada rama de código en onMeasure debe terminar con una llamada a setMeasuredDimension.

Ignorar el modo AT_MOST es el segundo error más frecuente. Si una View en modo AT_MOST siempre usa el tamaño pasado en lugar de calcular según el contenido, el contenedor padre no puede distribuir correctamente el espacio. Por ejemplo, un TextView en AT_MOST debe calcular el ancho del texto y usar el mínimo entre el ancho deseado y el pasado. Ignorar AT_MOST hace que la View ocupe todo el espacio disponible incluso con contenido pequeño.

Crear objetos dentro de onMeasure es un error clásico de rendimiento. Dado que onMeasure puede llamarse múltiples veces (en cada solicitud de layout), crear objetos (Paint, Rect, String) dentro de este método ensucia la memoria y provoca recolección de basura. Todos los objetos deben crearse una vez en el constructor de la View, y solo la lógica de cálculo de tamaños debe ejecutarse en onMeasure. La misma regla aplica a onDraw y onLayout.

Medición de Views hijas en ViewGroup

measureChildWithMargins es un método protegido de ViewGroup que mide una sola View hija considerando sus MarginLayoutParams. El método acepta el MeasureSpec del padre y los desplazamientos acumulados de ancho y alto. Ajusta automáticamente el MeasureSpec para la View hija restando los paddings del padre y los margins del hijo, luego pasa el MeasureSpec ajustado a child.measure().

Para una lógica de medición avanzada, una ViewGroup puede sobrescribir measureChild(View child, int parentWidthSpec, int parentHeightSpec) o trabajar directamente con MeasureSpec para cada hijo. Por ejemplo, LinearLayout en onMeasure itera a través de todas las Views hijas, mide cada una considerando su layout_weight y distribuye el espacio restante proporcionalmente. Este enfoque permite implementar algoritmos de diseño arbitrarios.

El almacenamiento en caché de los resultados de medición mediante el mecanismo de caché de medida está disponible a través de la bandera setMeasureWithLargestChildEnabled en ciertas ViewGroups. Sin embargo, en la mayoría de los casos onMeasure se llama nuevamente ante cualquier cambio de layout y el caché no se aplica. En ViewGroups personalizadas, se recomienda minimizar los cálculos en onMeasure en lugar de depender del caché.

Preguntas frecuentes

¿Es necesario sobrescribir onMeasure para una View personalizada?

, si la View personalizada hereda directamente de la clase View. Si hereda de TextView, ImageView o Button con sus tamaños estándar, onMeasure puede dejarse sin cambios. Para ViewGroup, sobrescribir onMeasure siempre es necesario — de lo contrario, los hijos no se medirán correctamente.

¿Qué sucede si no se llama a setMeasuredDimension?

El sistema Android lanza IllegalStateException con el mensaje “The View did not call setMeasuredDimension”. Esta excepción ocurre en el método measure() después de que onMeasure se completa, si las dimensiones finales quedaron en cero. La excepción bloquea la aplicación si no se maneja mediante try-catch.

¿Cuál es la diferencia entre getWidth y getMeasuredWidth?

getMeasuredWidth() devuelve el tamaño establecido en onMeasure (fase de medición). getWidth() devuelve el tamaño real que la View recibió en onLayout después de todos los ajustes de posicionamiento. Para la mayoría de las Views estos valores coinciden, pero en ViewGroups personalizadas pueden diferir.

¿Se pueden cambiar animaciones o el estado de la View dentro de onMeasure?

No, onMeasure está destinado exclusivamente al cálculo de tamaños. Cambiar el estado, iniciar animaciones, hacer networking o actualizar datos en este método viola la arquitectura de Android y puede provocar llamadas recursivas a measure, ya que los cambios de estado pueden desencadenar requestLayout.

¿Cómo interactúa onMeasure con ConstraintLayout?

ConstraintLayout gestiona la medición de los hijos de forma independiente según las restricciones definidas. Si una View personalizada dentro de ConstraintLayout sobrescribe onMeasure, debe manejar correctamente el MeasureSpec pasado desde ConstraintLayout, de lo contrario las restricciones podrían no funcionar. ConstraintLayout utiliza un algoritmo de dos pasos con su propio WidgetContainer para el cálculo.

Resumen

  • onMeasure() — el método de View para determinar dimensiones, llamado por el sistema Android durante la fase measure
  • MeasureSpec codifica el modo de medición (EXACTLY, AT_MOST, UNSPECIFIED) y el tamaño pasado desde el padre
  • setMeasuredDimension — una llamada obligatoria al final de onMeasure que establece las dimensiones finales
  • resolveSize — un método auxiliar que implementa la lógica estándar de manejo de MeasureSpec en una línea
  • Algoritmo de dos pasos garantiza una medición correcta en la jerarquía padre-hijo
  • measureChildWithMargins se usa para medir Views hijas en ViewGroups personalizadas con márgenes
  • Crear objetos dentro de onMeasure está totalmente desaconsejado debido al riesgo de GC y pérdida de fotogramas

Desarrollaremos una aplicación móvil llave en mano

IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.

Discutir el proyecto

Lea también