onLayout(): definizione, algoritmo di layout e parametri del metodo

Autore: IT Sectr Pubblicato: 2026-07-22 Tempo di lettura: 9 min

onLayout() è un metodo della classe ViewGroup che determina le posizioni e le dimensioni delle View figlie sul piano di coordinate del contenitore padre. Il sistema Android chiama onLayout dopo la fase di misurazione (onMeasure), quando la larghezza e l'altezza misurate sono già note per ogni View figlia. Secondo la Documentazione per Sviluppatori Android (2026), onLayout è un metodo obbligatorio da sovrascrivere in qualsiasi ViewGroup personalizzata, poiché l'implementazione standard di ViewGroup non esegue il posizionamento automatico dei figli.

Punti Chiave

  • onLayout(boolean changed, int l, int t, int r, int b) — metodo ViewGroup che determina le posizioni degli elementi figli nelle coordinate del padre
  • child.layout(l, t, r, b) — chiamata per ogni View figlia, impostando i suoi confini finali
  • La fase layout segue la fase measure e precede la fase draw nel ciclo di vita di View
  • getWidth() e getHeight() diventano disponibili solo dopo l'esecuzione di onLayout, a differenza di getMeasuredWidth disponibile dopo onMeasure
  • requestLayout() — metodo che avvia una chiamata ripetuta di onMeasure e onLayout quando cambiano i dati che influenzano il posizionamento

Cos'è onLayout()?

onLayout(boolean changed, int l, int t, int r, int b) è un metodo protected della classe ViewGroup che il sistema chiama per posizionare le View figlie all'interno del contenitore padre. Lo sviluppatore sovrascrive questo metodo quando crea una ViewGroup personalizzata con una disposizione non standard di elementi: a cascata, a griglia, a scacchiera o con coordinate arbitrarie. Ogni View figlia riceve i suoi confini finali tramite una chiamata a child.layout().

Il parametro changed indica se la posizione o la dimensione della ViewGroup stessa è cambiata dall'ultimo layout. Se changed è true, tutti gli elementi figli probabilmente necessitano di un riposizionamento. I parametri l, t, r, b sono le coordinate degli angoli superiore sinistro e inferiore destro della ViewGroup nel sistema di coordinate del suo padre. All'interno di onLayout, lo sviluppatore usa questi valori come coordinate iniziali per la disposizione dei figli.

ViewGroup è l'unica classe che sovrascrive onLayout. Una View normale (non ViewGroup) non ha elementi figli e non necessita di onLayout — il suo posizionamento è gestito dal contenitore padre. Anche se una View normale sovrascrive onLayout, il sistema non lo chiamerà. Questa è una differenza fondamentale da onMeasure, che viene chiamato per qualsiasi View.

Flusso della fase Layout

La fase layout inizia con una chiamata al metodo pubblico layout(int l, int t, int r, int b) sulla View radice. Questo metodo imposta le coordinate finali della View stessa e chiama onLayout se la View è una ViewGroup. Poi onLayout chiama ricorsivamente child.layout() per ogni elemento figlio, e il processo si ripete scendendo lungo la gerarchia. Così, il layout si propaga dalla radice alle foglie.

Prima di chiamare onLayout, il sistema verifica se le dimensioni della View sono cambiate rispetto al ciclo precedente. Se le dimensioni non sono cambiate e requestLayout non è stato chiamato, onLayout potrebbe non essere chiamato — il sistema usa i risultati del layout precedente. Questa è un'ottimizzazione che previene ricalcoli non necessari di posizioni durante animazioni o scorrimento, quando cambia solo il contenuto ma non le dimensioni.

requestLayout() è un metodo di View che notifica al sistema che il layout della View è obsoleto e deve essere ricalcolato. Chiamare requestLayout attiva un ciclo completo: prima viene chiamato onMeasure, poi onLayout, poi onDraw. A differenza di invalidate, che attiva solo il ridisegno, requestLayout attiva un ricalcolo completo di dimensioni e posizioni. Le chiamate eccessive a requestLayout sono una causa comune di problemi di prestazioni.

Parametri di onLayout: l, t, r, b

l (left) — la coordinata X del bordo sinistro della ViewGroup nel sistema di coordinate del suo padre. t (top) — la coordinata Y del bordo superiore. r (right) — la coordinata X del bordo destro. b (bottom) — la coordinata Y del bordo inferiore. La larghezza della ViewGroup è calcolata come r - l, l'altezza come b - t. Queste coordinate includono già tutto il padding della ViewGroup stessa.

All'interno di onLayout, lo sviluppatore chiama child.layout(int childLeft, int childTop, int childRight, int childBottom) per ogni View figlia. Le coordinate passate a child.layout devono essere nel sistema di coordinate della ViewGroup padre. Tipicamente childLeft e childTop sono calcolati tenendo conto del padding del padre: childLeft = l + paddingLeft + offsetX, childTop = t + paddingTop + offsetY.

ParametroDescrizioneUso Tipico
l (left)Coordinata del bordo sinistro della ViewGroup nel padrePunto iniziale sull'asse X per gli elementi figli
t (top)Coordinata del bordo superiore della ViewGroup nel padrePunto iniziale sull'asse Y per gli elementi figli
r (right)Coordinata del bordo destro della ViewGroup nel padreLimite superiore di larghezza, r - l = getWidth()
b (bottom)Coordinata del bordo inferiore della ViewGroup nel padreLimite superiore di altezza, b - t = getHeight()

Calcolo delle coordinate per gli elementi figli

Le coordinate figlie sono calcolate con la formula: childLeft = l + paddingLeft + (marginLeft se presente), childRight = childLeft + child.getMeasuredWidth(). Analogamente per l'asse verticale: childTop = t + paddingTop + (marginTop), childBottom = childTop + child.getMeasuredHeight(). Dopo aver calcolato questi quattro valori, viene chiamato child.layout(childLeft, childTop, childRight, childBottom).

Esempio di ViewGroup personalizzata con onLayout in Kotlin

Creiamo un FlowLayout — una ViewGroup personalizzata che dispone le View figlie in righe, spostando gli elementi su una nuova riga quando la riga corrente è piena. È un equivalente di Flexbox con wrap in un unico piano. onLayout itera attraverso tutte le View figlie, calcola la posizione per ciascuna e chiama child.layout() con i confini corretti.

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

Differenze tra onLayout e onMeasure

onMeasure e onLayout sono due fasi sequenziali del ciclo di vita di View che svolgono compiti fondamentalmente diversi. onMeasure determina le dimensioni desiderate (misurate) di una View, mentre onLayout imposta le coordinate e dimensioni effettive (finali). La differenza chiave: in onMeasure, le dimensioni possono essere intermedie e successivamente aggiustate dal padre, mentre in onLayout viene fissata la posizione finale di ogni View figlia.

onMeasure viene chiamato per ogni View, incluse le View foglia (TextView, ImageView, Button). onLayout viene chiamato solo per ViewGroup. Questo perché il posizionamento è responsabilità del contenitore padre, non della View stessa. Una View foglia riceve la sua posizione attraverso layout() chiamato dal onLayout del padre.

getMeasuredWidth() e getMeasuredHeight() sono disponibili dopo onMeasure, mentre getWidth() e getHeight() sono disponibili solo dopo onLayout. Se si accede a getWidth() all'interno di onMeasure, restituirà il valore del ciclo precedente o zero. Pertanto, per calcolare le dimensioni in onMeasure, è necessario usare MeasureSpec e i figli sequenzialmente.

Errori comuni quando si lavora con onLayout

Posizionamento senza considerare il padding — il primo errore nell'implementare onLayout. Lo sviluppatore spesso dimentica di aggiungere il paddingLeft e paddingTop del padre alle coordinate iniziali delle View figlie. Di conseguenza, i figli vengono visualizzati al bordo della ViewGroup, ignorando il padding impostato tramite setPadding() o nel markup XML. Calcolo corretto: childLeft = paddingLeft + offsetX.

Chiamare layout per figli invisibili — il secondo problema comune. Se una ViewGroup contiene View figlie con visibilità GONE, non è necessario posizionarle — non occupano spazio. Tuttavia, onLayout deve gestire correttamente questo caso, saltando i figli GONE. Per i figli INVISIBLE, è comunque necessario chiamare layout — mantengono il loro spazio anche se non vengono visualizzati.

Ignorare il parametro changed — il terzo errore. Il parametro changed indica se le dimensioni o la posizione della ViewGroup sono cambiate. Se changed == false, si possono usare coordinate memorizzate nella cache senza ricalcolare il layout di tutti gli elementi figli. Tuttavia, la memorizzazione completa del layout è un compito complesso, e nella maggior parte delle implementazioni onLayout ricalcola semplicemente tutti gli elementi ogni volta. Questo è accettabile con un numero ridotto di figli.

Domande frequenti

È possibile non sovrascrivere onLayout in ViewGroup?

Sì, è possibile se la ViewGroup utilizza LayoutParams standard e non aggiunge logica di posizionamento personalizzata. Tuttavia, l'implementazione standard di onLayout in ViewGroup non esegue alcuna azione — gli elementi figli non verranno posizionati. In pratica, tutte le ViewGroup (LinearLayout, RelativeLayout, FrameLayout) sovrascrivono onLayout.

Qual è la differenza tra layout e onLayout?

layout() è un metodo pubblico finale di View, chiamato dal sistema o dalla ViewGroup padre. Imposta le coordinate della View stessa e chiama onLayout se la View è una ViewGroup. onLayout() è un metodo protected che lo sviluppatore sovrascrive per la disposizione personalizzata degli elementi figli.

onLayout può chiamare requestLayout?

Tecnicamente — sì, può. Ma questo è fortemente sconsigliato, poiché porta a ricorsione infinita: requestLayout → onMeasure → onLayout → requestLayout. Se requestLayout viene chiamato all'interno di onLayout, il sistema lancerà un'eccezione StackOverflowError. Tutte le modifiche alle dimensioni devono essere eseguite prima di onLayout.

Come funziona onLayout con le animazioni?

Le animazioni di layout (LayoutTransition) intercettano i cambiamenti nelle posizioni delle View figlie e applicano un'animazione di transizione. Quando LayoutTransition è abilitato, onLayout imposta prima le posizioni finali, poi LayoutTransition anima il movimento dalla posizione vecchia a quella nuova. Questo richiede un'implementazione corretta di onLayout con coordinate finali appropriate.

Perché onLayout non viene chiamato dopo invalidate?

invalidate() attiva solo la fase draw (ridisegno), senza influenzare measure e layout. Per attivare onLayout, è necessario chiamare requestLayout(), che avvia il ciclo completo: measure → layout → draw. invalidate è più efficiente per aggiornare l'aspetto quando dimensioni e posizioni non cambiano.

Riepilogo

  • onLayout() — metodo ViewGroup che determina le posizioni finali delle View figlie dopo il completamento della fase di misurazione
  • child.layout(l, t, r, b) — il meccanismo principale per impostare le coordinate per ogni elemento figlio
  • Parametri l, t, r, b — coordinate dei bordi della ViewGroup nel sistema padre, larghezza = r - l, altezza = b - t
  • La fase layout si propaga ricorsivamente dalla View radice agli elementi figli, chiamando onLayout su ogni ViewGroup
  • requestLayout() attiva un ciclo completo di ricalcolo di dimensioni e posizioni, a differenza di invalidate che attiva solo il ridisegno
  • Considerare il padding in onLayout è obbligatorio — le coordinate iniziali dei figli devono includere paddingLeft e paddingTop del padre

Svilupperemo un'applicazione mobile chiavi in mano

IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.

Discuti il progetto

Leggi anche