onMeasure() — är en protected metod i klassen android.view.View som anropas av Android-systemet för att bestämma dimensionerna för en View. Systemet skickar två MeasureSpec-objekt till metoden, som var och en innehåller mätläget (EXACTLY, AT_MOST eller UNSPECIFIED) och den storlek som föreslås av den överordnade containern. Enligt Android Developers Documentation (2026) är åsidosättande av onMeasure med korrekt hantering av MeasureSpec ett obligatoriskt steg för alla anpassade vyer och ViewGroup som kräver exakt kontroll över dimensionerna.
Huvudpunkter
onMeasure(int widthMeasureSpec, int heightMeasureSpec) — är metoden i klassen View som anropas av Android-systemet för att bestämma bredden och höjden på vyn. Utvecklaren åsidosätter denna metod för att ange vilken storlek vyn ska ha baserat på de begränsningar som skickas i MeasureSpec. Utan korrekt åsidosättande av onMeasure kan en anpassad vy visas felaktigt eller inte visas alls.
Systemet anropar onMeasure i measure-fasen av vyens livscykel, som föregår layout- (onLayout) och draw- (onDraw) faserna. Om en vy inte åsidosätter onMeasure används implementeringen från superklassen, som ställer in standarddimensioner baserat på background drawable eller layout_params. Anropet super.onMeasure(widthMeasureSpec, heightMeasureSpec) fungerar bara för standard underklasser av View, som TextView eller ImageView.
Nyckelkravet för onMeasure — anropet setMeasuredDimension(int, int) måste obligatoriskt finnas i slutet av metoden. Om detta anrop saknas genererar systemet ett IllegalStateException-undantag med meddelandet att vyn inte fastställde uppmätta dimensioner. De slutliga dimensionerna blir tillgängliga via getter-metoderna getMeasuredWidth() och getMeasuredHeight() efter slutförandet av measure-fasen.
MeasureSpec — är ett 32-bitars heltal där de övre 2 bitarna kodar mätläget och de nedre 30 bitarna — storleken. Läget bestämmer hur fri vyn är att välja sin storlek. Android erbjuder tre lägen: EXACTLY, AT_MOST och UNSPECIFIED. Varje läge dikterar en annan bearbetningslogik i onMeasure.
| MeasureSpec-läge | Värde | Beteende |
|---|---|---|
| EXACTLY | Föräldern angav exakt storlek | Vyn måste passa exakt i den angivna storleken, om den inte vill överskrida gränserna |
| AT_MOST | Föräldern ställde in maximal storlek | Vyn kan välja vilken storlek som helst från 0 till det angivna maxvärdet |
| UNSPECIFIED | Föräldern inför inga begränsningar | Vyn kan välja vilken önskad storlek som helst utan övre begränsningar |
För att extrahera läge och storlek från MeasureSpec används de statiska metoderna i klassen MeasureSpec: MeasureSpec.getMode(int) returnerar ett av de tre lägena (EXACTLY, AT_MOST, UNSPECIFIED) och MeasureSpec.getSize(int) returnerar den numeriska storleken i pixlar. För att skapa en egen MeasureSpec används MeasureSpec.makeMeasureSpec(int size, int mode). Dessa tre metoder täcker alla scenarier för arbete med dimensioner i onMeasure.
Standardmönster för bearbetning av MeasureSpec: om läget är EXACTLY — använd den angivna storleken som slutgiltig; om AT_MOST — välj minimum av den önskade storleken (vyens innehåll) och det angivna maxvärdet; om UNSPECIFIED — använd vyens önskade storlek utan begränsningar. Detta mönster garanterar korrekt beteende under alla förälderns begränsningar.
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)
}
För att förenkla standardlogiken tillhandahåller Android metoden resolveSizeAndState, som tar emot den önskade storleken, MeasureSpec och returnerar den slutliga storleken med korrekt läge. Denna metod implementerar mönstret som beskrivs ovan på en rad kod. Även funktionen resolveSize(int size, int measureSpec) finns tillgänglig, som returnerar ren storlek utan tillståndsbitar.
Android använder en tvåpasseringsalgoritm för mätning (two-pass measure) som garanterar att varje vy i hierarkin får korrekta dimensioner med hänsyn till förälderns begränsningar och underordnade elements preferenser. I den första passeringen skickar föräldern MeasureSpec med begränsningar till de underordnade vyerna, och de underordnade vyerna beräknar sina önskade dimensioner. I den andra passeringen fattar föräldern det slutgiltiga beslutet om dimensionerna.
För ViewGroup är mätprocessen mer komplex: föräldern måste först mäta alla sina underordnade element och sedan bestämma sin egen storlek baserat på deras dimensioner. Anropet measureChildren(int widthMeasureSpec, int heightMeasureSpec) itererar genom alla underordnade vyer och anropar för varje measure(child, childWidthSpec, childHeightSpec). Efter att ha mätt alla underordnade element anropar ViewGroup setMeasuredDimension med sina egna dimensioner.
En viktig nyans: metoden measure (publik, final) kan inte åsidosättas — istället åsidosätts onMeasure. Detta garanterar att systemet kan utföra serviceåtgärder före och efter anropet av onMeasure, såsom kontroll av dimensionsändring och beräkning av smutsigt område för efterföljande ritning. Om en vy har fasta dimensioner kan åsidosättande av onMeasure inte krävas.
MeasureSpec inkluderar inte bara storlek och läge, utan även tillståndsbitar (state bits), tillgängliga via MeasureSpec.getMode(). Efter anropet av setMeasuredDimension blir tillståndet en del av vyens uppmätta dimensioner och kan kontrolleras via getMeasuredState(). Detta används i ScrollView och andra rullningsbara containrar för korrekt överföring av begränsningar till barn.
Låt oss titta på ett praktiskt exempel på att skapa en anpassad vy med åsidosättande av onMeasure för kvadratisk visning. Klassen SquareView ärver View och garanterar att bredd och höjd alltid är lika, oavsett de angivna MeasureSpec. I onMeasure bestäms den mindre sidan och den kvadratiska storleken ställs in.
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 kräver mer komplex onMeasure-logik eftersom först mäts de underordnade elementen, sedan bestäms storleken på själva ViewGroup. Exemplet CascadeLayout fördelar underordnade element i kaskad med mellanrum. Efter att ha mätt alla underordnade element via measureChildWithMargins beräknas total bredd och höjd.
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
}
}
}
Att hoppa över anropet till setMeasuredDimension — det vanligaste misstaget. Om utvecklaren åsidosatte onMeasure men inte anropade setMeasuredDimension kommer applikationen att krascha med IllegalStateException. Detta händer särskilt ofta när det finns villkorliga förgreningar i metoden och i en av grenarna saknas anropet. Varje kodgren i onMeasure måste avslutas med ett anrop till setMeasuredDimension.
Att ignorera AT_MOST-läget — det näst vanligaste misstaget. Om en vy i AT_MOST-läget alltid använder den angivna storleken istället för beräkning baserat på innehåll, kan den överordnade containern inte fördela utrymmet korrekt. Till exempel bör en TextView i AT_MOST beräkna textens bredd och använda minimum av den önskade och angivna bredden. Att ignorera AT_MOST leder till att vyn upptar allt tillgängligt utrymme även med litet innehåll.
Att skapa objekt inuti onMeasure — ett klassiskt prestandafel. Eftersom onMeasure kan anropas flera gånger (vid varje layoutförfrågan) skräpar skapandet av objekt (Paint, Rect, String) inuti denna metod ner minnet och utlöser garbage collection. Alla objekt bör skapas en gång i vyens konstruktor, och i onMeasure bör endast logiken för beräkning av dimensioner utföras. Samma regel gäller för onDraw och onLayout.
measureChildWithMargins — är en protected metod i ViewGroup som mäter en underordnad vy med hänsyn till dess MarginLayoutParams. Metoden tar emot förälderns MeasureSpec och ackumulerade förskjutningar i bredd och höjd. Den justerar automatiskt MeasureSpec för den underordnade vyn, subtraherar förälderns padding och barnets margin, och skickar den justerade MeasureSpec till child.measure().
För avancerad mätlogik kan ViewGroup åsidosätta measureChild(View child, int parentWidthSpec, int parentHeightSpec) eller arbeta direkt med MeasureSpec för varje underordnat element. Till exempel itererar LinearLayout i onMeasure genom alla underordnade vyer, mäter varje med hänsyn till dess layout_weight och fördelar det återstående utrymmet proportionellt. Detta tillvägagångssätt möjliggör implementering av godtyckliga layoutalgoritmer.
Cachning av mätresultat via mekanismen för mätningscache (measure cache) är tillgänglig via flaggan setMeasureWithLargestChildEnabled i vissa ViewGroup. I de flesta fall anropas dock onMeasure igen vid varje layoutändring och cachning tillämpas inte. I anpassade ViewGroup rekommenderas att minimera beräkningar i onMeasure, inte att förlita sig på cache.
Vanliga frågor
Ja, om den anpassade vyn ärver direkt från klassen View. Om ärvningen kommer från TextView, ImageView eller Button med deras standarddimensioner behöver onMeasure inte åsidosättas. För ViewGroup krävs alltid åsidosättande av onMeasure — annars kommer de underordnade elementen inte att mätas korrekt.
Android-systemet kastar IllegalStateException med texten “The View did not call setMeasuredDimension”. Detta undantag uppstår i metoden measure() efter slutförandet av onMeasure om de slutliga dimensionerna förblev noll. Undantaget kraschar applikationen om det inte hanteras via try-catch.
getMeasuredWidth() returnerar storleken som ställts in i onMeasure (mätningsfas). getWidth() returnerar den faktiska storleken som vyn fick i onLayout efter att ha tagit hänsyn till alla positionskorrigeringar. För de flesta vyer sammanfaller dessa värden, men i anpassade ViewGroup kan de skilja sig.
Nej, onMeasure är uteslutande avsett för beräkning av dimensioner. Att ändra tillstånd, starta animationer, arbeta med nätverket eller uppdatera data i denna metod bryter mot Android-arkitekturen och kan leda till rekursiva measure-anrop, eftersom tillståndsändring kan utlösa requestLayout.
ConstraintLayout hanterar självständigt mätningen av underordnade element baserat på givna begränsningar. Om en anpassad vy inuti ConstraintLayout åsidosätter onMeasure måste den korrekt bearbeta MeasureSpec som skickas från ConstraintLayout, annars kan begränsningarna kanske inte fungera. ConstraintLayout använder en tvåpasseringsalgoritm med sin egen WidgetContainer för beräkning.
Sammanfattning
Vi utvecklar en mobil applikation nyckelfärdigt
IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.
Läs också