onMeasure(): o que é, modos MeasureSpec e sobrescrita do método

Autor: IT Sectr Publicado: 2026-07-22 Tempo de leitura: 9 min

onMeasure() é um método protegido da classe android.view.View que o sistema Android chama para determinar o tamanho de uma View. O sistema passa dois objetos MeasureSpec para o método, cada um contendo um modo de medição (EXACTLY, AT_MOST ou UNSPECIFIED) e um tamanho sugerido pelo contêiner pai. De acordo com a Documentação para Desenvolvedores Android (2026), sobrescrever onMeasure com o tratamento correto de MeasureSpec é uma etapa obrigatória para todas as Views e ViewGroups personalizadas que exigem controle preciso de tamanho.

Pontos principais

  • onMeasure(int widthMeasureSpec, int heightMeasureSpec) — o método View para medir tamanhos com MeasureSpec passado do pai
  • MeasureSpec — um valor de 32 bits que codifica o modo de medição (UNSPECIFIED, EXACTLY, AT_MOST) e o tamanho
  • setMeasuredDimension(int w, int h) — uma chamada obrigatória dentro de onMeasure que define as dimensões finais da View
  • Algoritmo de duas passagens de medição: o pai mede os filhos, então os filhos informam seus tamanhos e o pai toma a decisão final
  • measureChildWithMargins — um método auxiliar para medir Views filhas em ViewGroups personalizadas

O que é onMeasure()?

onMeasure(int widthMeasureSpec, int heightMeasureSpec) é um método da classe View que o sistema Android chama para determinar a largura e altura de uma view. O desenvolvedor sobrescreve este método para especificar qual tamanho a View deve ter com base nas restrições passadas em MeasureSpec. Sem a sobrescrita correta de onMeasure, uma View personalizada pode ser exibida incorretamente ou nem aparecer.

O sistema chama onMeasure durante a fase measure do ciclo de vida da View, que precede as fases de layout (onLayout) e draw (onDraw). Se uma View não sobrescreve onMeasure, a implementação da superclasse é usada, que define tamanhos padrão com base no background drawable ou layout_params. Chamar super.onMeasure(widthMeasureSpec, heightMeasureSpec) funciona apenas para subclasses padrão de View, como TextView ou ImageView.

Um requisito fundamental de onMeasure é que a chamada a setMeasuredDimension(int, int) deve estar presente no final do método. Se esta chamada estiver faltando, o sistema lança uma IllegalStateException informando que a View não definiu as dimensões medidas. As dimensões finais ficam disponíveis através dos getters getMeasuredWidth() e getMeasuredHeight() após a conclusão da fase measure.

Modos MeasureSpec: três valores principais

MeasureSpec é um inteiro de 32 bits onde os 2 bits superiores codificam o modo de medição e os 30 bits inferiores codificam o tamanho. O modo determina o quão livre a View está para escolher seu próprio tamanho. O Android fornece três modos: EXACTLY, AT_MOST e UNSPECIFIED. Cada modo dita uma lógica de processamento diferente em onMeasure.

Modo MeasureSpecValorComportamento
EXACTLYO pai especificou um tamanho exatoA View deve se ajustar exatamente ao tamanho dado se não quiser ultrapassar os limites
AT_MOSTO pai definiu um tamanho máximoA View pode escolher qualquer tamanho de 0 ao máximo dado
UNSPECIFIEDO pai não impõe restriçõesA View pode escolher qualquer tamanho desejado sem limite superior

Para extrair o modo e o tamanho de MeasureSpec, são usados os métodos estáticos da classe MeasureSpec: MeasureSpec.getMode(int) retorna um dos três modos (EXACTLY, AT_MOST, UNSPECIFIED), e MeasureSpec.getSize(int) retorna o tamanho numérico em pixels. Para criar um MeasureSpec personalizado, usa-se MeasureSpec.makeMeasureSpec(int size, int mode). Estes três métodos cobrem todos os cenários de trabalho com tamanhos em onMeasure.

Lógica típica de tratamento de MeasureSpec

O padrão padrão para tratar MeasureSpec: se o modo é EXACTLY, use o tamanho dado como final; se é AT_MOST, escolha o mínimo entre o tamanho desejado (conteúdo da View) e o máximo dado; se é UNSPECIFIED, use o tamanho desejado da View sem restrições. Este padrão garante comportamento correto sob quaisquer restrições do pai.

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 a lógica padrão, o Android fornece o método resolveSizeAndState, que recebe o tamanho desejado, MeasureSpec e retorna o tamanho final com o modo correto. Este método implementa o padrão descrito acima em uma única linha de código. A função resolveSize(int size, int measureSpec) também está disponível, retornando um tamanho limpo sem bits de estado.

Algoritmo de medição de duas passagens

Android usa um algoritmo de medição de duas passagens que garante que cada View na hierarquia receba dimensões corretas considerando as restrições do pai e as preferências dos filhos. Na primeira passagem, o pai passa MeasureSpec com restrições para as Views filhas, e as Views filhas calculam seus tamanhos desejados. Na segunda passagem, o pai toma a decisão final sobre os tamanhos.

Para ViewGroup, o processo de medição é mais complexo: o pai deve primeiro medir todos os seus filhos, depois determinar seu próprio tamanho com base nos tamanhos deles. Chamar measureChildren(int widthMeasureSpec, int heightMeasureSpec) itera por todas as Views filhas e chama measure(child, childWidthSpec, childHeightSpec) para cada uma. Após medir todos os filhos, a ViewGroup chama setMeasuredDimension com suas próprias dimensões.

Uma nuance importante: o método measure (público, final) não pode ser sobrescrito — em vez disso, onMeasure é sobrescrito. Isso garante que o sistema possa realizar tarefas de manutenção antes e depois de onMeasure, como verificar mudanças de tamanho e calcular a área suja para desenho subsequente. Se uma View tem dimensões fixas, pode não ser necessário sobrescrever onMeasure.

Bandeira MEASURED_SIZE_STATE

MeasureSpec inclui não apenas o tamanho e o modo, mas também bits de estado, acessíveis via MeasureSpec.getMode(). Após chamar setMeasuredDimension, o estado se torna parte das dimensões medidas da View e pode ser verificado via getMeasuredState(). Isso é usado em ScrollView e outros contêineres roláveis para passar restrições corretamente aos filhos.

Exemplo de sobrescrita de onMeasure em Kotlin

Vamos examinar um exemplo prático de criação de uma View personalizada com onMeasure sobrescrito para exibição quadrada. A classe SquareView estende View e garante que largura e altura sejam sempre iguais, independentemente do MeasureSpec passado. Em onMeasure, o lado mínimo é determinado e o tamanho quadrado é definido.

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 com medição de filhos

ViewGroup requer lógica onMeasure mais complexa porque os filhos devem ser medidos primeiro, depois o tamanho da própria ViewGroup é determinado. O exemplo CascadeLayout distribui os filhos em cascata com um deslocamento. Após medir todos os filhos via measureChildWithMargins, a largura e altura totais são calculadas.

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

Erros comuns ao sobrescrever onMeasure

Falta da chamada setMeasuredDimension é o erro mais comum. Se um desenvolvedor sobrescreve onMeasure mas não chama setMeasuredDimension, o aplicativo falha com IllegalStateException. Isso acontece especialmente quando o método tem ramificações condicionais e uma delas não tem a chamada. Cada ramificação de código em onMeasure deve terminar com uma chamada a setMeasuredDimension.

Ignorar o modo AT_MOST é o segundo erro mais frequente. Se uma View no modo AT_MOST sempre usa o tamanho passado em vez de calcular com base no conteúdo, o contêiner pai não consegue distribuir o espaço corretamente. Por exemplo, um TextView em AT_MOST deve calcular a largura do texto e usar o mínimo entre a largura desejada e a passada. Ignorar AT_MOST faz a View ocupar todo o espaço disponível mesmo com conteúdo pequeno.

Criar objetos dentro de onMeasure é um erro clássico de desempenho. Como onMeasure pode ser chamado várias vezes (a cada solicitação de layout), criar objetos (Paint, Rect, String) dentro deste método polui a memória e aciona a coleta de lixo. Todos os objetos devem ser criados uma vez no construtor da View, e apenas a lógica de cálculo de tamanhos deve ser executada em onMeasure. A mesma regra se aplica a onDraw e onLayout.

Medição de Views filhas em ViewGroup

measureChildWithMargins é um método protegido de ViewGroup que mede uma única View filha considerando seus MarginLayoutParams. O método aceita o MeasureSpec do pai e os deslocamentos acumulados de largura e altura. Ele ajusta automaticamente o MeasureSpec para a View filha subtraindo os paddings do pai e os margins do filho, então passa o MeasureSpec ajustado para child.measure().

Para lógica de medição avançada, uma ViewGroup pode sobrescrever measureChild(View child, int parentWidthSpec, int parentHeightSpec) ou trabalhar diretamente com MeasureSpec para cada filho. Por exemplo, LinearLayout em onMeasure itera por todas as Views filhas, mede cada uma considerando seu layout_weight e distribui o espaço restante proporcionalmente. Esta abordagem permite implementar algoritmos de layout arbitrários.

O armazenamento em cache dos resultados de medição através do mecanismo de cache de medida está disponível através da bandeira setMeasureWithLargestChildEnabled em certas ViewGroups. No entanto, na maioria dos casos onMeasure é chamado novamente em qualquer mudança de layout e o cache não se aplica. Em ViewGroups personalizadas, recomenda-se minimizar os cálculos em onMeasure em vez de confiar no cache.

Perguntas frequentes

É necessário sobrescrever onMeasure para uma View personalizada?

Sim, se a View personalizada herda diretamente da classe View. Se herda de TextView, ImageView ou Button com seus tamanhos padrão, onMeasure pode ser deixado inalterado. Para ViewGroup, sobrescrever onMeasure é sempre necessário — caso contrário, os filhos não serão medidos corretamente.

O que acontece se setMeasuredDimension não for chamado?

O sistema Android lança IllegalStateException com a mensagem “The View did not call setMeasuredDimension”. Esta exceção ocorre no método measure() após a conclusão de onMeasure, se as dimensões finais permaneceram zero. A exceção quebra o aplicativo se não for tratada via try-catch.

Qual a diferença entre getWidth e getMeasuredWidth?

getMeasuredWidth() retorna o tamanho definido em onMeasure (fase de medição). getWidth() retorna o tamanho real que a View recebeu em onLayout após todos os ajustes de posicionamento. Para a maioria das Views esses valores coincidem, mas em ViewGroups personalizadas podem diferir.

Pode-se alterar animações ou estado da View dentro de onMeasure?

Não, onMeasure é destinado exclusivamente ao cálculo de tamanhos. Alterar estado, iniciar animações, fazer networking ou atualizar dados neste método viola a arquitetura do Android e pode levar a chamadas recursivas de measure, já que mudanças de estado podem acionar requestLayout.

Como onMeasure interage com ConstraintLayout?

ConstraintLayout gerencia a medição dos filhos independentemente com base nas restrições definidas. Se uma View personalizada dentro de ConstraintLayout sobrescreve onMeasure, ela deve tratar corretamente o MeasureSpec passado pelo ConstraintLayout, caso contrário as restrições podem não funcionar. ConstraintLayout usa um algoritmo de duas passagens com seu próprio WidgetContainer para cálculo.

Resumo

  • onMeasure() — o método View para determinar dimensões, chamado pelo sistema Android durante a fase measure
  • MeasureSpec codifica o modo de medição (EXACTLY, AT_MOST, UNSPECIFIED) e o tamanho passado do pai
  • setMeasuredDimension — uma chamada obrigatória no final de onMeasure que define as dimensões finais
  • resolveSize — um método auxiliar que implementa a lógica padrão de tratamento de MeasureSpec em uma linha
  • Algoritmo de duas passagens garante medição correta na hierarquia pai-filho
  • measureChildWithMargins é usado para medir Views filhas em ViewGroups personalizadas com margens
  • Criar objetos dentro de onMeasure é fortemente desaconselhado devido ao risco de GC e queda de quadros

Vamos desenvolver um aplicativo móvel chave na mão

A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.

Discutir o projeto

Leia também