onLayout() — är metoden i klassen ViewGroup som bestämmer positioner och storlekar för underordnade View på koordinatplanet för den föräldrade containern. Android-systemet anropar onLayout efter mätningsfasen (onMeasure), när för varje underordnat View redan är kända uppmätt bredd och höjd. Enligt Android Developers Documentation (2026) är onLayout en obligatorisk metod att åsidosätta i varje anpassad ViewGroup, eftersom standardimplementeringen av ViewGroup inte utför automatisk positionering av barn.
Huvudpunkter
onLayout(boolean changed, int l, int t, int r, int b) — är en protected metod i klassen ViewGroup som anropas av systemet för att positionera underordnade View inuti den föräldrade containern. Utvecklaren åsidosätter denna metod när han skapar en anpassad ViewGroup med en ostandardiserad arrangemang av element: i kaskad, rutnät, schackbrädesmönster eller enligt godtyckliga koordinater. Varje underordnat View får sina slutgiltiga gränser genom anropet child.layout().
Parametern changed anger om positionen eller storleken på själva ViewGroup har ändrats sedan den senaste layouten. Om changed är true behöver alla underordnade element troligen också ompositioneras. Parametrarna l, t, r, b är koordinaterna för ViewGroup-ets övre vänstra och nedre högra hörn i dess förälders koordinatsystem. Inuti onLayout använder utvecklaren dessa värden som startkoordinater för att placera barnen.
ViewGroup — är den enda klassen som åsidosätter onLayout. En vanlig View (inte ViewGroup) har inga underordnade element och behöver inte onLayout — dess egen positionering hanteras av den föräldrade containern. Även om en vanlig View åsidosätter onLayout kommer systemet inte att anropa den. Detta är den grundläggande skillnaden mellan onLayout och onMeasure, som anropas för varje View.
Layout-fasen börjar med anrop av den publika metoden layout(int l, int t, int r, int b) på rot-View. Denna metod anger de slutgiltiga koordinaterna för själva View och anropar onLayout om View är en ViewGroup. Därefter anropar onLayout rekursivt child.layout() för varje underordnat element, och processen upprepas nedåt i hierarkin. På så sätt sprids layout från roten till löven.
Före anrop av onLayout kontrollerar systemet om View-ets dimensioner har ändrats jämfört med föregående cykel. Om dimensionerna inte har ändrats och requestLayout inte har anropats, kanske onLayout inte anropas — systemet använder resultaten från den föregående layouten. Detta är en optimering som förhindrar onödiga omräkningar av positioner under animeringar eller scrollning, när endast innehållet ändras, inte dimensionerna.
requestLayout() — är en metod i View som informerar systemet om att View-ets layout är föråldrad och behöver omräknas. Anrop av requestLayout leder till en fullständig cykel: först anropas onMeasure, sedan onLayout, sedan onDraw. Till skillnad från invalidate, som endast startar omritning, startar requestLayout fullständig omräkning av dimensioner och positioner. Overdrivet anrop av requestLayout är en vanlig orsak till prestandaproblem.
l (left) — X-koordinaten för vänsterkanten av ViewGroup i dess förälders koordinatsystem. t (top) — Y-koordinaten för överkanten. r (right) — X-koordinaten för högerkanten. b (bottom) — Y-koordinaten för nederkanten. Bredden på ViewGroup beräknas som r - l, höjden som b - t. Dessa koordinater inkluderar redan all padding för själva ViewGroup.
Inuti onLayout anropar utvecklaren child.layout(int childLeft, int childTop, int childRight, int childBottom) för varje underordnat View. Koordinaterna som skickas till child.layout måste vara i den föräldrade ViewGroup-ets koordinatsystem. Vanligtvis beräknas childLeft och childTop med hänsyn till förälderns padding: childLeft = l + paddingLeft + offsetX, childTop = t + paddingTop + offsetY.
| Parameter | Beskrivning | Typisk användning |
|---|---|---|
| l (left) | Koordinat för vänsterkant av ViewGroup i förälder | Startpunkt på X-axel för underordnade element |
| t (top) | Koordinat för överkant av ViewGroup i förälder | Startpunkt på Y-axel för underordnade element |
| r (right) | Koordinat för högerkant av ViewGroup i förälder | Övre gräns för bredd, r - l = getWidth() |
| b (bottom) | Koordinat för nederkant av ViewGroup i förälder | Övre gräns för höjd, b - t = getHeight() |
Underordnade koordinater beräknas enligt formeln: childLeft = l + paddingLeft + (marginLeft if present), childRight = childLeft + child.getMeasuredWidth(). Liknande för vertikalt: childTop = t + paddingTop + (marginTop), childBottom = childTop + child.getMeasuredHeight(). Efter beräkning av dessa fyra värden anropas child.layout(childLeft, childTop, childRight, childBottom).
Låt oss skapa en FlowLayout — en anpassad ViewGroup som placerar underordnade View i rader och flyttar element till en ny rad när den aktuella raden är full. Detta är analogen till Flexbox med wrap i ett plan. onLayout itererar över alla underordnade View, beräknar positionen för varje och anropar child.layout() med korrekta gränser.
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)
}
onMeasure och onLayout — är två på varandra följande faser i View-ets livscykel som utför fundamentalt olika uppgifter. onMeasure bestämmer önskade (measured) dimensioner för View, medan onLayout anger faktiska (actual) koordinater och dimensioner. Den viktigaste skillnaden: i onMeasure kan dimensionerna vara tillfälliga och senare korrigeras av föräldern, medan i onLayout fastställs den slutgiltiga positionen för varje underordnat View.
onMeasure anropas för varje View, inklusive löv-View (TextView, ImageView, Button). onLayout anropas endast för ViewGroup. Detta förklaras av att positionering är den föräldrade containerns ansvar, inte själva View-ets. Ett löv-View får sin position genom layout() anropat från förälderns onLayout.
getMeasuredWidth() och getMeasuredHeight() är tillgängliga efter onMeasure, medan getWidth() och getHeight() — först efter onLayout. Om du kommer åt getWidth() inuti onMeasure kommer värdet från den föregående cykeln eller noll att returneras. Därför bör du använda MeasureSpec och successivt children för att beräkna dimensioner i onMeasure.
Positionering utan hänsyn till padding — första misstaget vid implementering av onLayout. Utvecklaren glömmer ofta att lägga till förälderns paddingLeft och paddingTop till startkoordinaterna för underordnade View. Som ett resultat visas barnen vid kanten av ViewGroup, utan att ta hänsyn till mellanrum som angetts via setPadding() eller i XML. Korrekt beräkning: childLeft = paddingLeft + offsetX.
Anropa layout för osynliga barn — det andra vanliga problemet. Om ViewGroup innehåller underordnade View med visibility lika med GONE, behöver de inte positioneras — de tar inte upp plats. Dock måste onLayout hantera detta fall korrekt genom att hoppa över GONE-barn. För INVISIBLE-barn måste layout fortfarande anropas — de behåller sin plats även om de inte visas.
Ignorera parametern changed — tredje misstaget. Parametern changed anger om ViewGroup-ets dimensioner eller position har ändrats. Om changed == false kan cachade koordinater användas och layout av alla underordnade element behöver inte omberäknas. Fullständig cachning av layout är dock en svår uppgift och de flesta implementeringar av onLayout omberäknar helt enkelt alla element varje gång. Detta är acceptabelt vid ett litet antal barn.
Vanliga frågor
Ja, om ViewGroup använder standard LayoutParams och inte lägger till någon anpassad positioneringslogik. Standardimplementeringen av onLayout i ViewGroup utför dock ingen åtgärd — underordnade element kommer inte att positioneras. I praktiken åsidosätter alla ViewGroup (LinearLayout, RelativeLayout, FrameLayout) onLayout.
layout() — är en publik final metod i View, anropad av systemet eller den föräldrade ViewGroup. Den anger koordinaterna för själva View och anropar onLayout om View är en ViewGroup. onLayout() — är en protected metod som åsidosätts av utvecklaren för anpassad placering av underordnade element.
Tekniskt — ja, det kan det. Men detta rekommenderas absolut inte eftersom det leder till oändlig rekursion: requestLayout → onMeasure → onLayout → requestLayout. Om requestLayout anropas inuti onLayout kommer systemet att kasta ett StackOverflowError-undantag. Alla storleksändringar bör göras före onLayout.
Layout-animationer (LayoutTransition) fångar ändringar i positioner för underordnade View och tillämpar en övergångsanimation. Med LayoutTransition aktiverad anger onLayout först de slutgiltiga positionerna, sedan animerar LayoutTransition förflyttningen från den gamla positionen till den nya. Detta kräver en korrekt implementering av onLayout med lämpliga slutkoordinater.
invalidate() startar endast draw-fasen (omritning) och påverkar inte measure och layout. För att utlösa onLayout måste du anropa requestLayout(), som startar en fullständig cykel: measure → layout → draw. invalidate är effektivare för att uppdatera utseendet när dimensioner och positioner inte ändras.
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å