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) é 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.
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 MeasureSpec | Valor | Comportamento |
|---|---|---|
| EXACTLY | O pai especificou um tamanho exato | A View deve se ajustar exatamente ao tamanho dado se não quiser ultrapassar os limites |
| AT_MOST | O pai definiu um tamanho máximo | A View pode escolher qualquer tamanho de 0 ao máximo dado |
| UNSPECIFIED | O pai não impõe restrições | A 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.
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.
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)
}
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.
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.
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.
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.
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 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.
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
}
}
}
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.
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
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 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.
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.
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.
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
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.
Leia também