onLayout(): o que é, algoritmo de layout e parâmetros do método

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

onLayout() é um método da classe ViewGroup que determina as posições e tamanhos das Views filhas no plano de coordenadas do contêiner pai. O sistema Android chama onLayout após a fase de medição (onMeasure), quando a largura e altura medidas já são conhecidas para cada View filha. De acordo com a Documentação para Desenvolvedores Android (2026), onLayout é um método obrigatório de sobrescrever em qualquer ViewGroup personalizada, pois a implementação padrão da ViewGroup não realiza o posicionamento automático dos filhos.

Pontos Principais

  • onLayout(boolean changed, int l, int t, int r, int b) — método ViewGroup que determina as posições dos elementos filhos nas coordenadas do pai
  • child.layout(l, t, r, b) — chamada para cada View filha, definindo seus limites finais
  • A fase layout segue a fase measure e precede a fase draw no ciclo de vida da View
  • getWidth() e getHeight() ficam disponíveis somente após a execução do onLayout, ao contrário de getMeasuredWidth que está disponível após onMeasure
  • requestLayout() — método que inicia uma chamada repetida de onMeasure e onLayout quando dados que afetam o posicionamento mudam

O que é onLayout()?

onLayout(boolean changed, int l, int t, int r, int b) é um método protected da classe ViewGroup que o sistema chama para posicionar as Views filhas dentro do contêiner pai. O desenvolvedor sobrescreve este método ao criar uma ViewGroup personalizada com uma disposição não padrão de elementos: cascata, grade, padrão xadrez ou por coordenadas arbitrárias. Cada View filha recebe seus limites finais através de uma chamada a child.layout().

O parâmetro changed indica se a posição ou tamanho da própria ViewGroup mudou desde o último layout. Se changed for true, todos os elementos filhos provavelmente também precisam de reposicionamento. Os parâmetros l, t, r, b são as coordenadas dos cantos superior esquerdo e inferior direito da ViewGroup no sistema de coordenadas do seu pai. Dentro de onLayout, o desenvolvedor usa estes valores como coordenadas iniciais para a disposição dos filhos.

ViewGroup é a única classe que sobrescreve onLayout. Uma View normal (não ViewGroup) não tem elementos filhos e não precisa de onLayout — seu posicionamento é tratado pelo contêiner pai. Mesmo que uma View normal sobrescreva onLayout, o sistema não o chamará. Esta é uma diferença fundamental do onMeasure, que é chamado para qualquer View.

Fluxo da Fase Layout

A fase layout começa com uma chamada ao método público layout(int l, int t, int r, int b) na View raiz. Este método define as coordenadas finais da própria View e chama onLayout se a View for uma ViewGroup. Então onLayout chama recursivamente child.layout() para cada elemento filho, e o processo se repete descendo na hierarquia. Assim, o layout se propaga da raiz para as folhas.

Antes de chamar onLayout, o sistema verifica se as dimensões da View mudaram em comparação com o ciclo anterior. Se as dimensões não mudaram e requestLayout não foi chamado, onLayout pode não ser chamado — o sistema usa os resultados do layout anterior. Esta é uma otimização que evita recálculos desnecessários de posições durante animações ou rolagem, quando apenas o conteúdo muda mas não as dimensões.

requestLayout() é um método de View que notifica o sistema que o layout da View está desatualizado e precisa ser recalculado. Chamar requestLayout desencadeia um ciclo completo: primeiro onMeasure é chamado, depois onLayout, depois onDraw. Ao contrário de invalidate, que apenas aciona o redesenhamento, requestLayout aciona um recálculo completo de dimensões e posições. Chamadas excessivas a requestLayout são uma causa comum de problemas de desempenho.

Parâmetros onLayout: l, t, r, b

l (left) — a coordenada X da borda esquerda da ViewGroup no sistema de coordenadas do seu pai. t (top) — a coordenada Y da borda superior. r (right) — a coordenada X da borda direita. b (bottom) — a coordenada Y da borda inferior. A largura da ViewGroup é calculada como r - l, a altura como b - t. Estas coordenadas já incluem todo o padding da própria ViewGroup.

Dentro de onLayout, o desenvolvedor chama child.layout(int childLeft, int childTop, int childRight, int childBottom) para cada View filha. As coordenadas passadas para child.layout devem estar no sistema de coordenadas da ViewGroup pai. Normalmente childLeft e childTop são calculados levando em conta o padding do pai: childLeft = l + paddingLeft + offsetX, childTop = t + paddingTop + offsetY.

ParâmetroDescriçãoUso Típico
l (left)Coordenada da borda esquerda da ViewGroup no paiPonto inicial no eixo X para elementos filhos
t (top)Coordenada da borda superior da ViewGroup no paiPonto inicial no eixo Y para elementos filhos
r (right)Coordenada da borda direita da ViewGroup no paiLimite superior de largura, r - l = getWidth()
b (bottom)Coordenada da borda inferior da ViewGroup no paiLimite superior de altura, b - t = getHeight()

Calculando Coordenadas para Elementos Filhos

As coordenadas filhas são calculadas pela fórmula: childLeft = l + paddingLeft + (marginLeft se presente), childRight = childLeft + child.getMeasuredWidth(). Similarmente para o eixo vertical: childTop = t + paddingTop + (marginTop), childBottom = childTop + child.getMeasuredHeight(). Após calcular estes quatro valores, child.layout(childLeft, childTop, childRight, childBottom) é chamado.

Exemplo de ViewGroup Personalizada com onLayout em Kotlin

Vamos criar um FlowLayout — uma ViewGroup personalizada que organiza as Views filhas em linhas, movendo elementos para uma nova linha quando a linha atual está cheia. É um equivalente do Flexbox com wrap em um único plano. onLayout itera por todas as Views filhas, calcula a posição para cada uma e chama child.layout() com os limites corretos.

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

    private val horizontalSpacing = 12
    private val verticalSpacing = 8

    override fun onMeasure(widthMeasureSpec: Int,
                       heightMeasureSpec: Int) {
        val parentWidth =
            MeasureSpec.getSize(widthMeasureSpec)
        var rowX = paddingLeft
        var rowY = paddingTop
        var maxRowHeight = 0

        for (i in 0 until childCount) {
            val child = getChildAt(i)
            measureChildWithMargins(child,
                widthMeasureSpec, 0,
                heightMeasureSpec, 0)

            if (rowX + child.measuredWidth >
                parentWidth - paddingRight) {
                rowX = paddingLeft
                rowY += maxRowHeight + verticalSpacing
                maxRowHeight = 0
            }

            rowX += child.measuredWidth +
                horizontalSpacing
            maxRowHeight = maxOf(maxRowHeight,
                child.measuredHeight)
        }

        val totalHeight = rowY + maxRowHeight +
            paddingBottom
        setMeasuredDimension(
            resolveSize(parentWidth, widthMeasureSpec),
            resolveSize(totalHeight, heightMeasureSpec))
    }

    override fun onLayout(changed: Boolean,
                       l: Int, t: Int,
                       r: Int, b: Int) {
        val parentWidth = r - l
        var rowX = paddingLeft
        var rowY = paddingTop
        var maxRowHeight = 0

        for (i in 0 until childCount) {
            val child = getChildAt(i)
            val cw = child.measuredWidth
            val ch = child.measuredHeight

            if (rowX + cw >
                parentWidth - paddingRight) {
                rowX = paddingLeft
                rowY += maxRowHeight + verticalSpacing
                maxRowHeight = 0
            }

            child.layout(rowX, rowY,
                rowX + cw, rowY + ch)
            rowX += cw + horizontalSpacing
            maxRowHeight =
                maxOf(maxRowHeight, ch)
        }
    }

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

Diferenças entre onLayout e onMeasure

onMeasure e onLayout são duas fases sequenciais do ciclo de vida da View que realizam tarefas fundamentalmente diferentes. onMeasure determina as dimensões desejadas (medidas) de uma View, enquanto onLayout define as coordenadas e dimensões reais (finais). A diferença chave: em onMeasure, as dimensões podem ser intermediárias e posteriormente ajustadas pelo pai, enquanto em onLayout a posição final de cada View filha é fixada.

onMeasure é chamado para cada View, incluindo Views folha (TextView, ImageView, Button). onLayout é chamado apenas para ViewGroup. Isso porque o posicionamento é responsabilidade do contêiner pai, não da própria View. Uma View folha recebe sua posição através de layout() chamado a partir do onLayout do pai.

getMeasuredWidth() e getMeasuredHeight() estão disponíveis após onMeasure, enquanto getWidth() e getHeight() estão disponíveis apenas após onLayout. Se você acessar getWidth() dentro de onMeasure, ele retornará o valor do ciclo anterior ou zero. Portanto, para calcular dimensões em onMeasure, você deve usar MeasureSpec e os filhos sequencialmente.

Erros Comuns ao Trabalhar com onLayout

Posicionamento sem levar em conta o padding — o primeiro erro ao implementar onLayout. O desenvolvedor frequentemente esquece de adicionar o paddingLeft e paddingTop do pai às coordenadas iniciais das Views filhas. Como resultado, os filhos são exibidos na borda da ViewGroup, ignorando o padding definido via setPadding() ou na marcação XML. Cálculo correto: childLeft = paddingLeft + offsetX.

Chamar layout para filhos invisíveis — o segundo problema comum. Se uma ViewGroup contém Views filhas com visibilidade GONE, elas não precisam ser posicionadas — não ocupam espaço. No entanto, onLayout deve lidar com este caso corretamente, pulando filhos GONE. Para filhos INVISIBLE, ainda é necessário chamar layout — eles mantêm seu espaço mesmo não sendo exibidos.

Ignorar o parâmetro changed — o terceiro erro. O parâmetro changed indica se as dimensões ou posição da ViewGroup mudaram. Se changed == false, coordenadas em cache podem ser usadas sem recalcular o layout de todos os elementos filhos. No entanto, o cache completo de layout é uma tarefa complexa, e na maioria das implementações onLayout simplesmente recalcula todos os elementos a cada vez. Isso é aceitável com um número pequeno de filhos.

Perguntas Frequentes

É possível não sobrescrever onLayout em ViewGroup?

Sim, é possível se a ViewGroup usar LayoutParams padrão e não adicionar lógica de posicionamento personalizada. No entanto, a implementação padrão de onLayout em ViewGroup não realiza nenhuma ação — os elementos filhos não serão posicionados. Na prática, todas as ViewGroup (LinearLayout, RelativeLayout, FrameLayout) sobrescrevem onLayout.

Qual é a diferença entre layout e onLayout?

layout() é um método público final de View, chamado pelo sistema ou ViewGroup pai. Ele define as coordenadas da própria View e chama onLayout se a View for uma ViewGroup. onLayout() é um método protected que o desenvolvedor sobrescreve para a disposição personalizada de elementos filhos.

Pode onLayout chamar requestLayout?

Tecnicamente — sim, pode. Mas isso não é recomendado, pois leva a recursão infinita: requestLayout → onMeasure → onLayout → requestLayout. Se requestLayout for chamado dentro de onLayout, o sistema lançará uma exceção StackOverflowError. Todas as alterações de dimensões devem ser feitas antes de onLayout.

Como onLayout funciona com animações?

Animações de layout (LayoutTransition) interceptam mudanças nas posições das Views filhas e aplicam animação de transição. Quando LayoutTransition está habilitado, onLayout primeiro define as posições finais, então LayoutTransition anima o movimento da posição antiga para a nova. Isso requer uma implementação correta de onLayout com coordenadas finais adequadas.

Por que onLayout não é chamado após invalidate?

invalidate() aciona apenas a fase draw (redesenhamento), sem afetar measure e layout. Para acionar onLayout, você precisa chamar requestLayout(), que inicia o ciclo completo: measure → layout → draw. invalidate é mais eficiente para atualizar a aparência quando dimensões e posições não mudam.

Resumo

  • onLayout() — método ViewGroup que determina as posições finais das Views filhas após a conclusão da fase de medição
  • child.layout(l, t, r, b) — o mecanismo principal para definir coordenadas para cada elemento filho
  • Parâmetros l, t, r, b — coordenadas das bordas da ViewGroup no sistema pai, largura = r - l, altura = b - t
  • A fase layout propaga-se recursivamente da View raiz para os elementos filhos, chamando onLayout em cada ViewGroup
  • requestLayout() aciona um ciclo completo de recálculo de dimensões e posições, ao contrário de invalidate que aciona apenas o redesenhamento
  • Levar em conta o padding em onLayout é obrigatório — as coordenadas iniciais dos filhos devem incluir paddingLeft e paddingTop do pai

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