onLayout(): Was es ist, Layout-Algorithmus und Methodenparameter

Autor: IT Sectr Veröffentlicht: 2026-07-22 Lesezeit: 9 Min.

onLayout() ist eine Methode der ViewGroup-Klasse, die die Positionen und Größen von Child-Views auf der Koordinatenebene des Elterncontainers bestimmt. Das Android-System ruft onLayout nach der Messphase (onMeasure) auf, wenn für jede Child-View die gemessene Breite und Höhe bereits bekannt sind. Laut Android Developers Documentation (2026) ist onLayout eine obligatorische Methode zum Überschreiben in jeder benutzerdefinierten ViewGroup, da die Standardimplementierung von ViewGroup keine automatische Positionierung der Kinder durchführt.

Wichtige Punkte

  • onLayout(boolean changed, int l, int t, int r, int b) — eine ViewGroup-Methode, die die Positionen der Kindelemente in Elternkoordinaten bestimmt
  • child.layout(l, t, r, b) — ein Aufruf für jede Child-View, der ihre endgültigen Grenzen festlegt
  • Die Layout-Phase folgt auf die Measure-Phase und geht der Draw-Phase im View-Lebenszyklus voraus
  • getWidth() und getHeight() sind erst nach der Ausführung von onLayout verfügbar, im Gegensatz zu getMeasuredWidth, das nach onMeasure verfügbar ist
  • requestLayout() — eine Methode, die bei Änderung positionierungsrelevanter Daten einen erneuten Aufruf von onMeasure und onLayout auslöst

Was ist onLayout()?

onLayout(boolean changed, int l, int t, int r, int b) ist eine protected-Methode der ViewGroup-Klasse, die vom System aufgerufen wird, um Child-Views innerhalb des Elterncontainers zu positionieren. Der Entwickler überschreibt diese Methode, wenn er eine benutzerdefinierte ViewGroup mit nicht standardmäßiger Elementanordnung erstellt: kaskadierend, gitterförmig, versetzt oder nach beliebigen Koordinaten. Jede Child-View erhält ihre endgültigen Grenzen durch einen Aufruf von child.layout().

Der Parameter changed gibt an, ob sich die Position oder Größe der ViewGroup selbst seit dem letzten Layout geändert hat. Wenn changed true ist, müssen wahrscheinlich alle Kindelemente neu positioniert werden. Die Parameter l, t, r, b sind die Koordinaten der oberen linken und unteren rechten Ecke der ViewGroup im Koordinatensystem ihres Eltern-Elements. Innerhalb von onLayout verwendet der Entwickler diese Werte als Startkoordinaten für die Anordnung der Kinder.

ViewGroup ist die einzige Klasse, die onLayout überschreibt. Eine normale View (keine ViewGroup) hat keine Kindelemente und benötigt kein onLayout — ihre Positionierung wird vom Elterncontainer übernommen. Selbst wenn eine normale View onLayout überschreibt, wird das System es nicht aufrufen. Dies ist ein grundlegender Unterschied zu onMeasure, das für jede View aufgerufen wird.

Ablauf der Layout-Phase

Die Layout-Phase beginnt mit einem Aufruf der öffentlichen Methode layout(int l, int t, int r, int b) auf der Root-View. Diese Methode setzt die endgültigen Koordinaten der View selbst und ruft onLayout auf, wenn die View eine ViewGroup ist. Dann ruft onLayout rekursiv child.layout() für jedes Kindelement auf, und der Prozess wiederholt sich die Hierarchie hinunter. Auf diese Weise propagiert sich das Layout von der Wurzel zu den Blättern.

Vor dem Aufruf von onLayout prüft das System, ob sich die Abmessungen der View im Vergleich zum vorherigen Zyklus geändert haben. Wenn sich die Abmessungen nicht geändert haben und requestLayout nicht aufgerufen wurde, wird onLayout möglicherweise nicht aufgerufen — das System verwendet die Ergebnisse des vorherigen Layouts. Dies ist eine Optimierung, die unnötige Positionsneuberechnungen während Animationen oder Scrollen verhindert, wenn sich nur der Inhalt ändert, nicht aber die Abmessungen.

requestLayout() ist eine View-Methode, die dem System mitteilt, dass das Layout der View veraltet ist und neu berechnet werden muss. Der Aufruf von requestLayout löst einen vollständigen Zyklus aus: Zuerst wird onMeasure aufgerufen, dann onLayout, dann onDraw. Im Gegensatz zu invalidate, das nur das Neuzeichnen auslöst, löst requestLayout eine vollständige Neuberechnung von Abmessungen und Positionen aus. Übermäßige Aufrufe von requestLayout sind eine häufige Ursache für Leistungsprobleme.

onLayout-Parameter: l, t, r, b

l (left) — die X-Koordinate des linken Rands der ViewGroup im Koordinatensystem ihres Eltern-Elements. t (top) — die Y-Koordinate des oberen Rands. r (right) — die X-Koordinate des rechten Rands. b (bottom) — die Y-Koordinate des unteren Rands. Die Breite der ViewGroup wird als r - l berechnet, die Höhe als b - t. Diese Koordinaten enthalten bereits das gesamte Padding der ViewGroup selbst.

Innerhalb von onLayout ruft der Entwickler child.layout(int childLeft, int childTop, int childRight, int childBottom) für jede Child-View auf. Die an child.layout übergebenen Koordinaten müssen im Koordinatensystem der Eltern-ViewGroup liegen. Normalerweise werden childLeft und childTop unter Berücksichtigung des Padding des Eltern-Elements berechnet: childLeft = l + paddingLeft + offsetX, childTop = t + paddingTop + offsetY.

ParameterBeschreibungTypische Verwendung
l (left)Koordinate des linken Rands der ViewGroup im Eltern-ElementStartpunkt auf der X-Achse für Kindelemente
t (top)Koordinate des oberen Rands der ViewGroup im Eltern-ElementStartpunkt auf der Y-Achse für Kindelemente
r (right)Koordinate des rechten Rands der ViewGroup im Eltern-ElementObere Breitengrenze, r - l = getWidth()
b (bottom)Koordinate des unteren Rands der ViewGroup im Eltern-ElementObere Höhengrenze, b - t = getHeight()

Berechnung von Koordinaten für Kindelemente

Kindkoordinaten werden nach der Formel berechnet: childLeft = l + paddingLeft + (marginLeft falls vorhanden), childRight = childLeft + child.getMeasuredWidth(). Analog für die vertikale Achse: childTop = t + paddingTop + (marginTop), childBottom = childTop + child.getMeasuredHeight(). Nach Berechnung dieser vier Werte wird child.layout(childLeft, childTop, childRight, childBottom) aufgerufen.

Beispiel für benutzerdefinierte ViewGroup mit onLayout in Kotlin

Erstellen wir ein FlowLayout — eine benutzerdefinierte ViewGroup, die Child-Views in Zeilen anordnet und Elemente in eine neue Zeile umbricht, wenn die aktuelle Zeile voll ist. Dies ist ein Pendant zu Flexbox mit Wrap in einer Ebene. onLayout durchläuft alle Child-Views, berechnet die Position für jede und ruft child.layout() mit korrekten Grenzen auf.

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

Unterschiede zwischen onLayout und onMeasure

onMeasure und onLayout sind zwei aufeinanderfolgende Phasen des View-Lebenszyklus, die grundlegend unterschiedliche Aufgaben ausführen. onMeasure bestimmt die gewünschten (gemessenen) Abmessungen einer View, während onLayout die tatsächlichen (endgültigen) Koordinaten und Abmessungen festlegt. Der Hauptunterschied: In onMeasure können Abmessungen vorläufig sein und später vom Eltern-Element angepasst werden, während in onLayout die endgültige Position jeder Child-View fixiert wird.

onMeasure wird für jede View aufgerufen, einschließlich Blatt-Views (TextView, ImageView, Button). onLayout wird nur für ViewGroup aufgerufen. Dies liegt daran, dass die Positionierung in der Verantwortung des Elterncontainers liegt, nicht der View selbst. Eine Blatt-View erhält ihre Position durch layout(), das vom onLayout des Eltern-Elements aufgerufen wird.

getMeasuredWidth() und getMeasuredHeight() sind nach onMeasure verfügbar, während getWidth() und getHeight() erst nach onLayout verfügbar sind. Wenn Sie innerhalb von onMeasure auf getWidth() zugreifen, wird der Wert des vorherigen Zyklus oder null zurückgegeben. Daher sollten Sie zur Berechnung von Abmessungen in onMeasure MeasureSpec und die Kinder sequenziell verwenden.

Häufige Fehler bei der Arbeit mit onLayout

Positionierung ohne Berücksichtigung von Padding — der erste Fehler bei der Implementierung von onLayout. Der Entwickler vergisst oft, das paddingLeft und paddingTop des Eltern-Elements zu den Startkoordinaten der Child-Views hinzuzufügen. Infolgedessen werden die Kinder am Rand der ViewGroup angezeigt und ignorieren das über setPadding() oder in XML-Markup gesetzte Padding. Korrekte Berechnung: childLeft = paddingLeft + offsetX.

Aufruf von Layout für unsichtbare Kinder — das zweite häufige Problem. Wenn eine ViewGroup Child-Views mit Sichtbarkeit GONE enthält, müssen sie nicht positioniert werden — sie nehmen keinen Platz ein. Allerdings muss onLayout diesen Fall korrekt behandeln, indem es GONE-Kinder überspringt. Für INVISIBLE-Kinder muss Layout weiterhin aufgerufen werden — sie behalten ihren Platz, auch wenn sie nicht angezeigt werden.

Ignorieren des changed-Parameters — der dritte Fehler. Der changed-Parameter gibt an, ob sich die Abmessungen oder die Position der ViewGroup geändert haben. Wenn changed == false ist, können zwischengespeicherte Koordinaten verwendet werden, ohne das Layout aller Kindelemente neu zu berechnen. Allerdings ist die vollständige Layout-Zwischenspeicherung eine komplexe Aufgabe, und in den meisten Implementierungen berechnet onLayout einfach jedes Mal alle Elemente neu. Dies ist bei einer kleinen Anzahl von Kindern akzeptabel.

Häufig gestellte Fragen

Kann man onLayout in ViewGroup nicht überschreiben?

Ja, das ist möglich, wenn die ViewGroup Standard-LayoutParams verwendet und keine benutzerdefinierte Positionierungslogik hinzufügt. Allerdings führt die Standardimplementierung von onLayout in ViewGroup keine Aktionen aus — die Kindelemente werden nicht positioniert. In der Praxis überschreiben alle ViewGroup (LinearLayout, RelativeLayout, FrameLayout) onLayout.

Was ist der Unterschied zwischen layout und onLayout?

layout() ist eine öffentliche finale Methode von View, die vom System oder der Eltern-ViewGroup aufgerufen wird. Sie setzt die Koordinaten der View selbst und ruft onLayout auf, wenn die View eine ViewGroup ist. onLayout() ist eine protected-Methode, die der Entwickler für die benutzerdefinierte Anordnung von Kindelementen überschreibt.

Kann onLayout requestLayout aufrufen?

Technisch — ja, kann es. Dies wird jedoch dringend nicht empfohlen, da es zu einer Endlosschleife führt: requestLayout → onMeasure → onLayout → requestLayout. Wenn requestLayout innerhalb von onLayout aufgerufen wird, löst das System eine StackOverflowError-Ausnahme aus. Alle Dimensionsänderungen sollten vor onLayout durchgeführt werden.

Wie arbeitet onLayout mit Animationen?

Layout-Animationen (LayoutTransition) fangen Änderungen der Positionen von Child-Views ab und wenden Übergangsanimationen an. Wenn LayoutTransition aktiviert ist, setzt onLayout zunächst die endgültigen Positionen, dann animiert LayoutTransition die Bewegung von der alten zur neuen Position. Dies erfordert eine korrekte onLayout-Implementierung mit geeigneten Endkoordinaten.

Warum wird onLayout nach invalidate nicht aufgerufen?

invalidate() löst nur die Draw-Phase (Neuzeichnen) aus, ohne Measure und Layout zu beeinflussen. Um onLayout auszulösen, müssen Sie requestLayout() aufrufen, das den vollständigen Zyklus startet: measure → layout → draw. invalidate ist effizienter zum Aktualisieren des Erscheinungsbilds, wenn sich Abmessungen und Positionen nicht ändern.

Zusammenfassung

  • onLayout() — eine ViewGroup-Methode, die die endgültigen Positionen der Child-Views nach Abschluss der Messphase bestimmt
  • child.layout(l, t, r, b) — der primäre Mechanismus zum Setzen von Koordinaten für jedes Kindelement
  • Parameter l, t, r, b — Koordinaten der Ränder der ViewGroup im Elternsystem, Breite = r - l, Höhe = b - t
  • Die Layout-Phase propagiert sich rekursiv von der Root-View zu den Kindelementen und ruft onLayout auf jeder ViewGroup auf
  • requestLayout() löst einen vollständigen Neuberechnungszyklus von Abmessungen und Positionen aus, im Gegensatz zu invalidate, das nur das Neuzeichnen auslöst
  • Berücksichtigung von Padding in onLayout ist obligatorisch — die Startkoordinaten der Kinder müssen paddingLeft und paddingTop des Eltern-Elements enthalten

Wir entwickeln eine mobile Applikation schlüsselfertig

IT Sectr entwickelt seit 2017 iOS- und Android-Apps für Startups und Unternehmen. Wir beraten Sie und schlagen die beste Lösung vor.

Projekt besprechen

Lesen Sie auch