onMeasure() è un metodo protetto della classe android.view.View che il sistema Android chiama per determinare le dimensioni di una View. Il sistema passa due oggetti MeasureSpec al metodo, ciascuno contenente una modalità di misurazione (EXACTLY, AT_MOST o UNSPECIFIED) e una dimensione suggerita dal contenitore genitore. Secondo la Documentazione per Sviluppatori Android (2026), l'override di onMeasure con la corretta gestione di MeasureSpec è un passaggio obbligatorio per tutte le View e ViewGroup personalizzate che richiedono un controllo preciso delle dimensioni.
Punti chiave
onMeasure(int widthMeasureSpec, int heightMeasureSpec) è un metodo della classe View che il sistema Android chiama per determinare la larghezza e l'altezza di una vista. Lo sviluppatore esegue l'override di questo metodo per specificare quale dimensione deve avere la View in base ai vincoli passati in MeasureSpec. Senza un corretto override di onMeasure, una View personalizzata potrebbe visualizzarsi in modo errato o non apparire affatto.
Il sistema chiama onMeasure durante la fase measure del ciclo di vita della View, che precede le fasi di layout (onLayout) e draw (onDraw). Se una View non esegue l'override di onMeasure, viene utilizzata l'implementazione della superclasse, che imposta dimensioni predefinite basate sul background drawable o su layout_params. Chiamare super.onMeasure(widthMeasureSpec, heightMeasureSpec) funziona solo per sottoclassi standard di View come TextView o ImageView.
Un requisito fondamentale di onMeasure è che la chiamata a setMeasuredDimension(int, int) deve essere presente alla fine del metodo. Se questa chiamata manca, il sistema lancia una IllegalStateException indicando che la View non ha impostato le dimensioni misurate. Le dimensioni finali diventano disponibili tramite i getter getMeasuredWidth() e getMeasuredHeight() dopo il completamento della fase measure.
MeasureSpec è un intero a 32 bit in cui i 2 bit superiori codificano la modalità di misurazione e i 30 bit inferiori codificano la dimensione. La modalità determina quanto è libera la View di scegliere la propria dimensione. Android fornisce tre modalità: EXACTLY, AT_MOST e UNSPECIFIED. Ciascuna modalità detta una logica di elaborazione diversa in onMeasure.
| Modalità MeasureSpec | Valore | Comportamento |
|---|---|---|
| EXACTLY | Il genitore ha specificato una dimensione esatta | La View deve adattarsi esattamente alla dimensione data se non vuole superare i limiti |
| AT_MOST | Il genitore ha impostato una dimensione massima | La View può scegliere qualsiasi dimensione da 0 al massimo dato |
| UNSPECIFIED | Il genitore non impone vincoli | La View può scegliere qualsiasi dimensione desiderata senza limite superiore |
Per estrarre la modalità e la dimensione da MeasureSpec, si usano i metodi statici della classe MeasureSpec: MeasureSpec.getMode(int) restituisce una delle tre modalità (EXACTLY, AT_MOST, UNSPECIFIED) e MeasureSpec.getSize(int) restituisce la dimensione numerica in pixel. Per creare un MeasureSpec personalizzato, si usa MeasureSpec.makeMeasureSpec(int size, int mode). Questi tre metodi coprono tutti gli scenari di lavoro con le dimensioni in onMeasure.
Il pattern standard per gestire MeasureSpec: se la modalità è EXACTLY, usa la dimensione passata come finale; se è AT_MOST, scegli il minimo tra la dimensione desiderata (il contenuto della View) e il massimo passato; se è UNSPECIFIED, usa la dimensione desiderata della View senza vincoli. Questo pattern garantisce un comportamento corretto sotto qualsiasi vincolo del genitore.
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)
}
Per semplificare la logica standard, Android fornisce il metodo resolveSizeAndState, che prende la dimensione desiderata, MeasureSpec e restituisce la dimensione finale con la modalità corretta. Questo metodo implementa il pattern descritto sopra in una singola riga di codice. È disponibile anche la funzione resolveSize(int size, int measureSpec), che restituisce una dimensione pulita senza bit di stato.
Android utilizza un algoritmo di misurazione a due passaggi che garantisce che ogni View nella gerarchia riceva dimensioni corrette considerando i vincoli del genitore e le preferenze dei figli. Al primo passaggio, il genitore passa MeasureSpec con vincoli alle View figlie e le View figlie calcolano le loro dimensioni desiderate. Al secondo passaggio, il genitore prende la decisione finale sulle dimensioni.
Per ViewGroup, il processo di misurazione è più complesso: il genitore deve prima misurare tutti i suoi figli, poi determinare la propria dimensione in base alle loro dimensioni. Chiamare measureChildren(int widthMeasureSpec, int heightMeasureSpec) itera attraverso tutte le View figlie e chiama measure(child, childWidthSpec, childHeightSpec) per ciascuna. Dopo aver misurato tutti i figli, la ViewGroup chiama setMeasuredDimension con le proprie dimensioni.
Una sfumatura importante: il metodo measure (pubblico, final) non può essere sovrascritto — invece viene sovrascritto onMeasure. Ciò garantisce che il sistema possa eseguire attività di manutenzione prima e dopo onMeasure, come il controllo dei cambiamenti di dimensione e il calcolo dell'area sporca per il disegno successivo. Se una View ha dimensioni fisse, potrebbe non essere necessario sovrascrivere onMeasure.
MeasureSpec include non solo la dimensione e la modalità, ma anche bit di stato, accessibili tramite MeasureSpec.getMode(). Dopo aver chiamato setMeasuredDimension, lo stato diventa parte delle dimensioni misurate della View e può essere verificato tramite getMeasuredState(). Ciò viene utilizzato in ScrollView e altri contenitori scorrevoli per passare correttamente i vincoli ai figli.
Esaminiamo un esempio pratico di creazione di una View personalizzata con override di onMeasure per la visualizzazione quadrata. La classe SquareView estende View e garantisce che larghezza e altezza siano sempre uguali, indipendentemente dal MeasureSpec passato. In onMeasure, viene determinato il lato minimo e viene impostata la dimensione quadrata.
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 richiede una logica onMeasure più complessa perché prima devono essere misurati i figli, poi viene determinata la dimensione della ViewGroup stessa. L'esempio CascadeLayout distribuisce i figli a cascata con un offset. Dopo aver misurato tutti i figli tramite measureChildWithMargins, vengono calcolate la larghezza e l'altezza totali.
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
}
}
}
Mancata chiamata a setMeasuredDimension è l'errore più comune. Se uno sviluppatore sovrascrive onMeasure ma non chiama setMeasuredDimension, l'applicazione si blocca con IllegalStateException. Ciò accade soprattutto quando il metodo ha rami condizionali e in uno di essi manca la chiamata. Ogni ramo di codice in onMeasure deve terminare con una chiamata a setMeasuredDimension.
Ignorare la modalità AT_MOST è il secondo errore più frequente. Se una View in modalità AT_MOST usa sempre la dimensione passata invece di calcolare in base al contenuto, il contenitore genitore non può distribuire correttamente lo spazio. Ad esempio, un TextView in AT_MOST dovrebbe calcolare la larghezza del testo e usare il minimo tra la larghezza desiderata e quella passata. Ignorare AT_MOST fa sì che la View occupi tutto lo spazio disponibile anche con contenuto piccolo.
Creare oggetti all'interno di onMeasure è un classico errore di prestazioni. Poiché onMeasure può essere chiamato più volte (ad ogni richiesta di layout), creare oggetti (Paint, Rect, String) all'interno di questo metodo intasa la memoria e innesca la garbage collection. Tutti gli oggetti dovrebbero essere creati una volta nel costruttore della View, e solo la logica di calcolo delle dimensioni dovrebbe essere eseguita in onMeasure. La stessa regola si applica a onDraw e onLayout.
measureChildWithMargins è un metodo protetto di ViewGroup che misura una singola View figlia considerando i suoi MarginLayoutParams. Il metodo accetta il MeasureSpec del genitore e gli offset accumulati di larghezza e altezza. Regola automaticamente il MeasureSpec per la View figlia sottraendo i padding del genitore e i margin del figlio, quindi passa il MeasureSpec regolato a child.measure().
Per una logica di misurazione avanzata, una ViewGroup può sovrascrivere measureChild(View child, int parentWidthSpec, int parentHeightSpec) o lavorare direttamente con MeasureSpec per ogni figlio. Ad esempio, LinearLayout in onMeasure itera attraverso tutte le View figlie, misura ciascuna considerando il suo layout_weight e distribuisce lo spazio rimanente proporzionalmente. Questo approccio consente di implementare algoritmi di layout arbitrari.
La memorizzazione nella cache dei risultati di misurazione tramite il meccanismo di cache di misura è disponibile attraverso il flag setMeasureWithLargestChildEnabled in determinate ViewGroup. Tuttavia, nella maggior parte dei casi onMeasure viene richiamato ad ogni modifica del layout e la cache non viene applicata. Nelle ViewGroup personalizzate, si raccomanda di minimizzare i calcoli in onMeasure piuttosto che fare affidamento sulla cache.
Domande frequenti
Sì, se la View personalizzata eredita direttamente dalla classe View. Se eredita da TextView, ImageView o Button con le loro dimensioni standard, onMeasure può rimanere invariato. Per ViewGroup, l'override di onMeasure è sempre necessario — altrimenti i figli non verranno misurati correttamente.
Il sistema Android lancia IllegalStateException con il messaggio “The View did not call setMeasuredDimension”. Questa eccezione si verifica nel metodo measure() dopo il completamento di onMeasure, se le dimensioni finali sono rimaste pari a zero. L'eccezione blocca l'applicazione se non gestita tramite try-catch.
getMeasuredWidth() restituisce la dimensione impostata in onMeasure (fase di misurazione). getWidth() restituisce la dimensione effettiva che la View ha ricevuto in onLayout dopo tutte le regolazioni di posizionamento. Per la maggior parte delle View questi valori coincidono, ma in ViewGroup personalizzate possono differire.
No, onMeasure è destinato esclusivamente al calcolo delle dimensioni. Modificare lo stato, avviare animazioni, fare networking o aggiornare dati in questo metodo viola l'architettura di Android e può portare a chiamate ricorsive di measure, poiché i cambiamenti di stato possono attivare requestLayout.
ConstraintLayout gestisce la misurazione dei figli in modo indipendente in base ai vincoli definiti. Se una View personalizzata all'interno di ConstraintLayout sovrascrive onMeasure, deve gestire correttamente il MeasureSpec passato da ConstraintLayout, altrimenti i vincoli potrebbero non funzionare. ConstraintLayout utilizza un algoritmo a due passaggi con il proprio WidgetContainer per il calcolo.
Riepilogo
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.
Leggi anche