onLayout(): co to je, algoritmus rozmístění a parametry metody

Autor: IT Sectr Publikováno: 2026-07-22 Doba čtení: 9 min

onLayout() — je metoda třídy ViewGroup, která určuje pozice a rozměry dceřiných View na rovině souřadnic rodičovského kontejneru. Systém Android volá onLayout po fázi měření (onMeasure), když jsou pro každé dceřiné View již známy naměřená šířka a výška. Podle Android Developers Documentation (2026) je onLayout povinnou metodou pro přepsání v každé vlastní ViewGroup, protože standardní implementace ViewGroup neprovádí automatické pozicování potomků.

Hlavní body

  • onLayout(boolean changed, int l, int t, int r, int b) — metoda ViewGroup určující pozice dceřiných prvků v souřadnicích rodiče
  • child.layout(l, t, r, b) — volání pro každé dceřiné View, které nastavuje jeho konečné hranice
  • Fáze layout následuje po fázi measure a před fází draw v životním cyklu View
  • getWidth() a getHeight() jsou k dispozici až po provedení onLayout, na rozdíl od getMeasuredWidth po onMeasure
  • requestLayout() — metoda iniciující opětovné volání onMeasure a onLayout při změně dat ovlivňujících pozicování

Co je onLayout()?

onLayout(boolean changed, int l, int t, int r, int b) — je chráněná metoda třídy ViewGroup, kterou systém volá pro pozicování dceřiných View uvnitř rodičovského kontejneru. Vývojář tuto metodu přepisuje, když vytváří vlastní ViewGroup s nestandardním uspořádáním prvků: kaskádou, mříží, šachovnicovým vzorem nebo podle libovolných souřadnic. Každé dceřiné View získává své konečné hranice prostřednictvím volání child.layout().

Parametr changed udává, zda se pozice nebo velikost samotné ViewGroup změnila od posledního layout. Pokud je changed true, všechny dceřiné prvky pravděpodobně také vyžadují přepozicování. Parametry l, t, r, b jsou souřadnice levého horního a pravého dolního rohu ViewGroup v souřadném systému jejího rodiče. Uvnitř onLayout vývojář používá tyto hodnoty jako počáteční souřadnice pro rozmístění potomků.

ViewGroup — je jediná třída, která přepisuje onLayout. Běžný View (není ViewGroup) nemá dceřiné prvky a nepotřebuje onLayout — jeho vlastní pozicování je řešeno rodičovským kontejnerem. I když běžný View přepíše onLayout, systém jej nebude volat. To je základní rozdíl mezi onLayout a onMeasure, který je volán pro každý View.

Průběh fáze layout

Fáze layout začíná voláním veřejné metody layout(int l, int t, int r, int b) na kořenovém View. Tato metoda nastavuje konečné souřadnice samotného View a volá onLayout, pokud je View ViewGroup. Poté onLayout rekurzivně volá child.layout() pro každý dceřiný prvek a proces se opakuje směrem dolů hierarchií. Tímto způsobem se layout šíří od kořene k listům.

Před voláním onLayout systém zkontroluje, zda se rozměry View změnily oproti předchozímu cyklu. Pokud se rozměry nezměnily a requestLayout nebyl volán, onLayout nemusí být volán — systém použije výsledky předchozího layout. To je optimalizace, která zabraňuje zbytečným přepočtům pozic během animací nebo scrollování, když se mění pouze obsah, nikoli rozměry.

requestLayout() — je metoda View, která informuje systém, že layout View je zastaralý a vyžaduje přepočet. Volání requestLayout vede k úplnému cyklu: nejprve se volá onMeasure, poté onLayout, poté onDraw. Na rozdíl od invalidate, který spouští pouze překreslení, requestLayout spouští úplný přepočet rozměrů a pozic. Nadměrné volání requestLayout je častou příčinou problémů s výkonem.

Parametry onLayout: l, t, r, b

l (left) — souřadnice X levého okraje ViewGroup v souřadném systému jejího rodiče. t (top) — souřadnice Y horního okraje. r (right) — souřadnice X pravého okraje. b (bottom) — souřadnice Y dolního okraje. Šířka ViewGroup se vypočítá jako r - l, výška jako b - t. Tyto souřadnice již zahrnují všechna padding samotné ViewGroup.

Uvnitř onLayout vývojář volá child.layout(int childLeft, int childTop, int childRight, int childBottom) pro každé dceřiné View. Souřadnice předané do child.layout musí být v souřadném systému rodičovské ViewGroup. Obvykle se childLeft a childTop vypočítávají s ohledem na padding rodiče: childLeft = l + paddingLeft + offsetX, childTop = t + paddingTop + offsetY.

ParametrPopisTypické použití
l (left)Souřadnice levého okraje ViewGroup v rodičiPočáteční bod na ose X pro dceřiné prvky
t (top)Souřadnice horního okraje ViewGroup v rodičiPočáteční bod na ose Y pro dceřiné prvky
r (right)Souřadnice pravého okraje ViewGroup v rodičiHorní hranice šířky, r - l = getWidth()
b (bottom)Souřadnice dolního okraje ViewGroup v rodičiHorní hranice výšky, b - t = getHeight()

Výpočet souřadnic pro dceřiné prvky

Souřadnice potomka se vypočítají podle vzorce: childLeft = l + paddingLeft + (marginLeft if present), childRight = childLeft + child.getMeasuredWidth(). Podobně pro vertikálu: childTop = t + paddingTop + (marginTop), childBottom = childTop + child.getMeasuredHeight(). Po výpočtu těchto čtyř hodnot se zavolá child.layout(childLeft, childTop, childRight, childBottom).

Příklad vlastní ViewGroup s onLayout v Kotlinu

Vytvořme FlowLayout — vlastní ViewGroup, která umísťuje dceřiné View do řádků, přesouvá prvky na nový řádek, když je aktuální řádek plný. Jedná se o analogii Flexbox s wrapem v jedné rovině. onLayout iteruje všechny dceřiné View, vypočítá pozici pro každý a volá child.layout() se správnými hranicemi.

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

Rozdíly mezi onLayout a onMeasure

onMeasure a onLayout — jsou dvě po sobě jdoucí fáze životního cyklu View, které vykonávají zásadně odlišné úkoly. onMeasure určuje požadované (measured) rozměry View, zatímco onLayout nastavuje skutečné (actual) souřadnice a rozměry. Klíčový rozdíl: v onMeasure mohou být rozměry dočasné a později opravené rodičem, zatímco v onLayout je fixována konečná pozice každého dceřiného View.

onMeasure je volán pro každý View, včetně listových (TextView, ImageView, Button). onLayout je volán pouze pro ViewGroup. To je způsobeno tím, že pozicování je odpovědností rodičovského kontejneru, nikoli samotného View. Listový View získává svou pozici prostřednictvím layout() volaného z rodičovského onLayout.

getMeasuredWidth() a getMeasuredHeight() jsou k dispozici po onMeasure, zatímco getWidth() a getHeight() — až po onLayout. Pokud přistoupíte k getWidth() uvnitř onMeasure, bude vrácena hodnota z předchozího cyklu nebo nula. Proto pro výpočet rozměrů v onMeasure použijte MeasureSpec a postupně children.

Typické chyby při práci s onLayout

Pozicování bez ohledu na padding — první chyba při implementaci onLayout. Vývojář často zapomíná přidat paddingLeft a paddingTop rodiče k počátečním souřadnicím dceřiných View. V důsledku toho se děti zobrazují na okraji ViewGroup, ignorující mezery nastavené pomocí setPadding() nebo v XML. Správný výpočet: childLeft = paddingLeft + offsetX.

Volání layout pro neviditelné potomky — druhý častý problém. Pokud ViewGroup obsahuje dceřiné View s visibility rovným GONE, není třeba je pozicovat — nezabírají místo. Nicméně onLayout musí tento případ správně zpracovat, přeskakovat GONE potomky. Pro INVISIBLE potomky je třeba layout stále volat — zachovávají své místo, i když nejsou zobrazeny.

Ignorování parametru changed — třetí chyba. Parametr changed udává, zda se rozměry nebo pozice ViewGroup změnily. Pokud changed == false, lze použít kešované souřadnice a nepřepočítávat layout všech dceřiných prvků. Úplné kešování layout je však obtížný úkol a většina implementací onLayout jednoduše přepočítává všechny prvky pokaždé. To je přijatelné při malém počtu potomků.

Často kladené otázky

Mohu nepřepsat onLayout ve ViewGroup?

Můžete, pokud ViewGroup používá standardní LayoutParams a nepřidává vlastní logiku pozicování. Standardní implementace onLayout ve ViewGroup však neprovádí žádnou akci — dceřiné prvky nebudou pozicovány. V praxi všechny ViewGroup (LinearLayout, RelativeLayout, FrameLayout) přepisují onLayout.

Jaký je rozdíl mezi layout a onLayout?

layout() — je veřejná konečná metoda View, volaná systémem nebo rodičovskou ViewGroup. Nastavuje souřadnice samotného View a volá onLayout, pokud je View ViewGroup. onLayout() — je chráněná metoda, kterou vývojář přepisuje pro vlastní uspořádání dceřiných prvků.

Může onLayout zavolat requestLayout?

Technicky — ano, může. To je však kategoricky nedoporučeno, protože vede k nekonečné rekurzi: requestLayout → onMeasure → onLayout → requestLayout. Pokud je requestLayout zavolán uvnitř onLayout, systém vyhodí výjimku StackOverflowError. Všechny změny rozměrů by měly být provedeny před onLayout.

Jak onLayout pracuje s animacemi?

Layout animace (LayoutTransition) zachycují změny pozic dceřiných View a aplikují přechodovou animaci. Při zapnuté LayoutTransition onLayout nejprve nastaví konečné pozice a poté LayoutTransition animuje přesun ze staré pozice do nové. To vyžaduje správnou implementaci onLayout s odpovídajícími konečnými souřadnicemi.

Proč se onLayout nevolá po invalidate?

invalidate() spouští pouze fázi draw (překreslení) a neovlivňuje measure a layout. Pro spuštění onLayout je třeba zavolat requestLayout(), který spouští úplný cyklus: measure → layout → draw. invalidate je účinnější pro aktualizaci vzhledu, když se rozměry a pozice nemění.

Shrnutí

  • onLayout() — metoda ViewGroup určující konečné pozice dceřiných View po dokončení fáze měření
  • child.layout(l, t, r, b) — hlavní mechanismus nastavení souřadnic pro každý dceřiný prvek
  • Parametry l, t, r, b — souřadnice okrajů ViewGroup v rodičovském systému, šířka = r - l, výška = b - t
  • Fáze layout se rekurzivně šíří od kořenového View k dceřiným prvkům, volajíc onLayout na každé ViewGroup
  • requestLayout() spouští úplný cyklus přepočtu rozměrů a pozic, na rozdíl od invalidate, který spouští pouze překreslení
  • Zohlednění padding v onLayout je povinné — počáteční souřadnice potomků musí zahrnovat paddingLeft a paddingTop rodiče

Vyvineme mobilní aplikaci na klíč

IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.

Prodiskutovat projekt

Přečtěte si také