onLayout(): wat het is, het algoritme voor plaatsing en methodeparameters

Auteur: IT Sectr Gepubliceerd: 2026-07-22 Leestijd: 9 min

onLayout() — is de methode van de klasse ViewGroup die de posities en afmetingen van kind-View's op het coördinatenvlak van de bovenliggende container bepaalt. Het Android-systeem roept onLayout aan na de meetfase (onMeasure), wanneer voor elke kind-View de gemeten breedte en hoogte al bekend zijn. Volgens de Android Developers Documentation (2026) is onLayout een verplichte methode om te overschrijven in elke aangepaste ViewGroup, omdat de standaardimplementatie van ViewGroup geen automatische positionering van kinderen uitvoert.

Belangrijkste punten

  • onLayout(boolean changed, int l, int t, int r, int b) — ViewGroup-methode die posities van onderliggende elementen bepaalt in coördinaten van de ouder
  • child.layout(l, t, r, b) — aanroep voor elke kind-View die de uiteindelijke grenzen instelt
  • Layout-fase volgt na de measure-fase en vóór de draw-fase in de levenscyclus van View
  • getWidth() en getHeight() worden pas beschikbaar na het uitvoeren van onLayout, in tegenstelling tot getMeasuredWidth na onMeasure
  • requestLayout() — methode die een hernieuwde aanroep van onMeasure en onLayout initieert bij wijziging van gegevens die de positionering beïnvloeden

Wat is onLayout()?

onLayout(boolean changed, int l, int t, int r, int b) — is een protected methode van de klasse ViewGroup die door het systeem wordt aangeroepen om kind-View's binnen de bovenliggende container te positioneren. De ontwikkelaar overschrijft deze methode wanneer hij een aangepaste ViewGroup maakt met een niet-standaard rangschikking van elementen: in cascade, raster, schaakbordpatroon of op willekeurige coördinaten. Elke kind-View ontvangt zijn uiteindelijke grenzen via de aanroep child.layout().

Parameter changed geeft aan of de positie of grootte van de ViewGroup zelf is veranderd sinds de laatste layout. Als changed waar is, moeten alle onderliggende elementen waarschijnlijk ook worden herpositioneerd. De parameters l, t, r, b zijn de coördinaten van de linker bovenhoek en rechter onderhoek van de ViewGroup in het coördinatenstelsel van de ouder. Binnen onLayout gebruikt de ontwikkelaar deze waarden als begincoördinaten voor het plaatsen van kinderen.

ViewGroup — is de enige klasse die onLayout overschrijft. Een gewone View (geen ViewGroup) heeft geen onderliggende elementen en heeft geen onLayout nodig — zijn eigen positionering wordt afgehandeld door de bovenliggende container. Zelfs als een gewone View onLayout overschrijft, zal het systeem deze niet aanroepen. Dit is het fundamentele verschil tussen onLayout en onMeasure, dat voor elke View wordt aangeroepen.

Uitvoeringsstroom van de layout-fase

De layout-fase begint met de aanroep van de publieke methode layout(int l, int t, int r, int b) op de root-View. Deze methode stelt de uiteindelijke coördinaten van de View zelf in en roept onLayout aan als de View een ViewGroup is. Vervolgens roept onLayout recursief child.layout() aan voor elk onderliggend element, en het proces herhaalt zich naar beneden in de hiërarchie. Op deze manier verspreidt layout zich van de wortel naar de bladeren.

Vóór de aanroep van onLayout controleert het systeem of de afmetingen van de View zijn veranderd ten opzichte van de vorige cyclus. Als de afmetingen niet zijn veranderd en requestLayout niet is aangeroepen, wordt onLayout mogelijk niet aangeroepen — het systeem gebruikt de resultaten van de vorige layout. Dit is een optimalisatie die onnodige herberekeningen van posities tijdens animaties of scrollen voorkomt, wanneer alleen de inhoud verandert en niet de afmetingen.

requestLayout() — is een methode van View die het systeem informeert dat de layout van de View verouderd is en opnieuw moet worden berekend. Het aanroepen van requestLayout leidt tot een volledige cyclus: eerst wordt onMeasure aangeroepen, dan onLayout, dan onDraw. In tegenstelling tot invalidate, dat alleen het opnieuw tekenen start, start requestLayout de volledige herberekening van afmetingen en posities. Overmatig aanroepen van requestLayout is een veelvoorkomende oorzaak van prestatieproblemen.

Parameters van onLayout: l, t, r, b

l (left) — de X-coördinaat van de linkerrand van de ViewGroup in het coördinatenstelsel van de ouder. t (top) — de Y-coördinaat van de bovenrand. r (right) — de X-coördinaat van de rechterrand. b (bottom) — de Y-coördinaat van de onderrand. De breedte van de ViewGroup wordt berekend als r - l, de hoogte als b - t. Deze coördinaten omvatten reeds alle padding van de ViewGroup zelf.

Binnen onLayout roept de ontwikkelaar child.layout(int childLeft, int childTop, int childRight, int childBottom) aan voor elke kind-View. De coördinaten die aan child.layout worden doorgegeven, moeten in het coördinatenstelsel van de bovenliggende ViewGroup zijn. Gewoonlijk worden childLeft en childTop berekend rekening houdend met de padding van de ouder: childLeft = l + paddingLeft + offsetX, childTop = t + paddingTop + offsetY.

ParameterBeschrijvingTypisch gebruik
l (left)Coördinaat van linkerrand ViewGroup in ouderStartpunt op X-as voor onderliggende elementen
t (top)Coördinaat van bovenrand ViewGroup in ouderStartpunt op Y-as voor onderliggende elementen
r (right)Coördinaat van rechterrand ViewGroup in ouderBovengrens breedte, r - l = getWidth()
b (bottom)Coördinaat van onderrand ViewGroup in ouderBovengrens hoogte, b - t = getHeight()

Berekening van coördinaten voor onderliggende elementen

Coördinaten van kinderen worden berekend volgens de formule: childLeft = l + paddingLeft + (marginLeft if present), childRight = childLeft + child.getMeasuredWidth(). Analoog voor verticaal: childTop = t + paddingTop + (marginTop), childBottom = childTop + child.getMeasuredHeight(). Na het berekenen van deze vier waarden wordt child.layout(childLeft, childTop, childRight, childBottom) aangeroepen.

Voorbeeld van aangepaste ViewGroup met onLayout in Kotlin

Laten we een FlowLayout maken — een aangepaste ViewGroup die kind-View's in rijen plaatst en elementen naar een nieuwe rij verplaatst wanneer de huidige rij vol is. Dit is het equivalent van Flexbox met wrap in één vlak. onLayout doorloopt alle kind-View's, berekent de positie voor elke en roept child.layout() aan met de juiste grenzen.

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

Verschillen tussen onLayout en onMeasure

onMeasure en onLayout — zijn twee opeenvolgende fasen in de levenscyclus van View die fundamenteel verschillende taken uitvoeren. onMeasure bepaalt de gewenste (measured) afmetingen van de View, terwijl onLayout de werkelijke (actual) coördinaten en afmetingen vaststelt. Het belangrijkste verschil: in onMeasure kunnen afmetingen tijdelijk zijn en later door de ouder worden gecorrigeerd, terwijl in onLayout de definitieve positie van elke kind-View wordt vastgelegd.

onMeasure wordt aangeroepen voor elke View, inclusief blad-Views (TextView, ImageView, Button). onLayout wordt alleen aangeroepen voor ViewGroup. Dit wordt verklaard doordat positionering de verantwoordelijkheid is van de bovenliggende container, niet van de View zelf. Een blad-View ontvangt zijn positie via layout() aangeroepen vanuit de onLayout van de ouder.

getMeasuredWidth() en getMeasuredHeight() zijn beschikbaar na onMeasure, terwijl getWidth() en getHeight() pas na onLayout beschikbaar zijn. Als u getWidth() binnen onMeasure aanroept, wordt de waarde van de vorige cyclus of nul teruggegeven. Daarom moet u voor het berekenen van afmetingen in onMeasure MeasureSpec en achtereenvolgens children gebruiken.

Veelvoorkomende fouten bij het werken met onLayout

Positionering zonder rekening te houden met padding — de eerste fout bij het implementeren van onLayout. De ontwikkelaar vergeet vaak om paddingLeft en paddingTop van de ouder toe te voegen aan de begincoördinaten van kind-View's. Als gevolg worden children aan de rand van de ViewGroup weergegeven, waarbij de marges die zijn ingesteld via setPadding() of in XML worden genegeerd. Correcte berekening: childLeft = paddingLeft + offsetX.

Layout aanroepen voor onzichtbare kinderen — het tweede veelvoorkomende probleem. Als een ViewGroup kind-View's bevat met visibility gelijk aan GONE, hoeven deze niet te worden gepositioneerd — ze nemen geen ruimte in. Echter, onLayout moet dit geval correct afhandelen door GONE-kinderen over te slaan. Voor INVISIBLE-kinderen moet layout nog steeds worden aangeroepen — ze behouden hun plaats, hoewel ze niet worden weergegeven.

Het negeren van de parameter changed — de derde fout. De parameter changed geeft aan of de afmetingen of positie van de ViewGroup zijn veranderd. Als changed == false, kunnen gecachede coördinaten worden gebruikt en hoeft de layout van alle onderliggende elementen niet opnieuw te worden berekend. Echter, het volledig cachen van layout is een moeilijke taak en in de meeste implementaties van onLayout worden eenvoudigweg alle elementen elke keer opnieuw berekend. Dit is acceptabel bij een klein aantal kinderen.

Veelgestelde vragen

Kan ik onLayout in een ViewGroup overslaan?

Ja, als de ViewGroup standaard LayoutParams gebruikt en geen aangepaste positioneringslogica toevoegt. Echter, de standaardimplementatie van onLayout in ViewGroup voert geen acties uit — onderliggende elementen worden niet gepositioneerd. In de praktijk overschrijven alle ViewGroups (LinearLayout, RelativeLayout, FrameLayout) onLayout.

Wat is het verschil tussen layout en onLayout?

layout() — is een publieke final methode van View, aangeroepen door het systeem of de bovenliggende ViewGroup. Het stelt de coördinaten van de View zelf in en roept onLayout aan als de View een ViewGroup is. onLayout() — is een protected methode die door de ontwikkelaar wordt overschreven voor het aangepast plaatsen van onderliggende elementen.

Kan onLayout requestLayout aanroepen?

Technisch — ja, het kan. Maar dit wordt ten zeerste afgeraden omdat het leidt tot oneindige recursie: requestLayout → onMeasure → onLayout → requestLayout. Als requestLayout binnen onLayout wordt aangeroepen, gooit het systeem een StackOverflowError. Alle wijzigingen in afmetingen moeten vóór onLayout worden uitgevoerd.

Hoe werkt onLayout met animaties?

Layout-animaties (LayoutTransition) onderscheppen wijzigingen in posities van kind-View's en passen een overgangsanimatie toe. Met ingeschakelde LayoutTransition stelt onLayout eerst de uiteindelijke posities in, waarna LayoutTransition de verplaatsing van de oude naar de nieuwe positie animeert. Dit vereist een correcte implementatie van onLayout met de juiste eindcoördinaten.

Waarom wordt onLayout niet aangeroepen na invalidate?

invalidate() start alleen de draw-fase (opnieuw tekenen) en heeft geen invloed op measure en layout. Om onLayout te activeren, moet requestLayout() worden aangeroepen, wat een volledige cyclus start: measure → layout → draw. invalidate is efficiënter voor het bijwerken van het uiterlijk wanneer afmetingen en posities niet veranderen.

Samenvatting

  • onLayout() — ViewGroup-methode die de uiteindelijke posities van kind-View's bepaalt na afronding van de meetfase
  • child.layout(l, t, r, b) — het belangrijkste mechanisme voor het instellen van coördinaten voor elk onderliggend element
  • Parameters l, t, r, b — coördinaten van de randen van ViewGroup in het ouderlijke stelsel, breedte = r - l, hoogte = b - t
  • Layout-fase verspreidt zich recursief van de root-View naar onderliggende elementen, waarbij onLayout op elke ViewGroup wordt aangeroepen
  • requestLayout() start een volledige cyclus van herberekening van afmetingen en posities, in tegenstelling tot invalidate dat alleen het opnieuw tekenen start
  • Rekening houden met padding in onLayout is verplicht — de begincoördinaten van kinderen moeten paddingLeft en paddingTop van de ouder bevatten

We ontwikkelen een mobiele applicatie turnkey

IT Sectr creëert sinds 2017 iOS- en Android-applicaties voor startups en bedrijven. We adviseren u en stellen de beste oplossing voor.

Bespreek het project

Lees ook