onMeasure(): co to je, režimy MeasureSpec a přepsání metody

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

onMeasure() — je protected metoda třídy android.view.View, kterou systém Android volá k určení rozměrů View. Systém předává metodě dva objekty MeasureSpec, z nichž každý obsahuje režim měření (EXACTLY, AT_MOST nebo UNSPECIFIED) a velikost navrženou rodičovským kontejnerem. Podle Android Developers Documentation (2026) je přepsání onMeasure se správným zpracováním MeasureSpec povinným krokem pro všechna vlastní View a ViewGroup vyžadující přesnou kontrolu nad rozměry.

Hlavní body

  • onMeasure(int widthMeasureSpec, int heightMeasureSpec) — metoda View pro měření rozměrů s předáním MeasureSpec od rodiče
  • MeasureSpec — 32-bitová hodnota kódující režim měření (UNSPECIFIED, EXACTLY, AT_MOST) a velikost
  • setMeasuredDimension(int w, int h) — povinné volání uvnitř onMeasure, které fixuje konečné rozměry View
  • Dvouprůchodový algoritmus měření: rodič měří děti, poté děti nahlásí své rozměry a rodič přijme konečné rozhodnutí
  • measureChildWithMargins — pomocná metoda pro měření podřízených View ve vlastních ViewGroup

Co je onMeasure()?

onMeasure(int widthMeasureSpec, int heightMeasureSpec) — je metoda třídy View, kterou systém Android volá k určení šířky a výšky zobrazení. Vývojář přepisuje tuto metodu, aby určil, jakou velikost má mít View na základě omezení předaných v MeasureSpec. Bez správného přepsání onMeasure se může vlastní View zobrazovat nesprávně nebo se nezobrazit vůbec.

Systém volá onMeasure ve fázi measure životního cyklu View, která předchází fázím layout (onLayout) a draw (onDraw). Pokud View nepřepisuje onMeasure, používá se implementace z nadtřídy, která nastaví výchozí rozměry na základě background drawable nebo layout_params. Volání super.onMeasure(widthMeasureSpec, heightMeasureSpec) funguje pouze pro standardní podtřídy View, jako je TextView nebo ImageView.

Klíčovým požadavkem pro onMeasure — volání setMeasuredDimension(int, int) musí být povinně přítomno na konci metody. Pokud toto volání chybí, systém generuje výjimku IllegalStateException se zprávou, že View nezaznamenal změřené rozměry. Konečné rozměry jsou k dispozici přes gettery getMeasuredWidth() a getMeasuredHeight() po dokončení fáze measure.

Režimy MeasureSpec: tři klíčové hodnoty

MeasureSpec — je 32-bitové celé číslo, ve kterém horní 2 bity kódují režim měření a spodních 30 bitů — velikost. Režim určuje, jak volný je View ve výběru své velikosti. Android poskytuje tři režimy: EXACTLY, AT_MOST a UNSPECIFIED. Každý režim diktuje jinou logiku zpracování v onMeasure.

Režim MeasureSpecHodnotaChování
EXACTLYRodič určil přesnou velikostView se musí přesně vejít do předané velikosti, pokud nechce překročit hranice
AT_MOSTRodič nastavil maximální velikostView může zvolit libovolnou velikost od 0 do předaného maxima
UNSPECIFIEDRodič neukládá omezeníView může zvolit libovolnou požadovanou velikost bez horních omezení

Pro extrakci režimu a velikosti z MeasureSpec se používají statické metody třídy MeasureSpec: MeasureSpec.getMode(int) vrací jeden ze tří režimů (EXACTLY, AT_MOST, UNSPECIFIED) a MeasureSpec.getSize(int) vrací číselnou velikost v pixelech. Pro vytvoření vlastního MeasureSpec se používá MeasureSpec.makeMeasureSpec(int size, int mode). Tyto tři metody pokrývají všechny scénáře práce s rozměry v onMeasure.

Typická logika zpracování MeasureSpec

Standardní vzor zpracování MeasureSpec: pokud je režim EXACTLY — použijte předanou velikost jako konečnou; pokud AT_MOST — vyberte minimum z požadované velikosti (obsahu View) a předaného maxima; pokud UNSPECIFIED — použijte požadovanou velikost View bez omezení. Tento vzor zaručuje správné chování při jakýchkoli omezeních rodiče.

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

Použití resolveSize

Pro zjednodušení standardní logiky poskytuje Android metodu resolveSizeAndState, která přijímá požadovanou velikost, MeasureSpec a vrací konečnou velikost se správným režimem. Tato metoda implementuje výše popsaný vzor v jednom řádku kódu. K dispozici je také funkce resolveSize(int size, int measureSpec), která vrací čistou velikost bez stavových bitů.

Dvouprůchodový algoritmus měření

Android používá dvouprůchodový algoritmus měření (two-pass measure), který zaručuje, že každé View v hierarchii obdrží správné rozměry s ohledem na omezení rodiče a preference podřízených prvků. V prvním průchodu rodič předá podřízeným View MeasureSpec s omezeními a podřízená View vypočítají své požadované rozměry. Ve druhém průchodu rodič přijme konečné rozhodnutí o rozměrech.

Pro ViewGroup je proces měření složitější: rodič musí nejprve změřit všechny své podřízené prvky a poté určit svou vlastní velikost na základě jejich rozměrů. Volání measureChildren(int widthMeasureSpec, int heightMeasureSpec) iteruje přes všechna podřízená View a volá pro každé measure(child, childWidthSpec, childHeightSpec). Po změření všech podřízených prvků ViewGroup zavolá setMeasuredDimension se svými rozměry.

Důležitá nuance: metoda measure (veřejná, final) nelze přepsat — místo ní se přepisuje onMeasure. To zaručuje, že systém může provádět servisní akce před a po volání onMeasure, jako je kontrola změny rozměrů a výpočet špinavé oblasti pro následné kreslení. Pokud má View pevné rozměry, přepsání onMeasure nemusí být vyžadováno.

Příznak MEASURED_SIZE_STATE

MeasureSpec zahrnuje nejen velikost a režim, ale také stavové bity (state bits), přístupné přes MeasureSpec.getMode(). Po volání setMeasuredDimension se stav stane součástí změřených rozměrů View a lze jej zkontrolovat přes getMeasuredState(). To se používá v ScrollView a dalších posouvatelných kontejnerech pro správné předávání omezení dětem.

Příklad přepsání onMeasure v Kotlinu

Podívejme se na praktický příklad vytvoření vlastního View s přepsáním onMeasure pro čtvercové zobrazení. Třída SquareView dědí View a zaručuje, že šířka a výška jsou vždy stejné, bez ohledu na předané MeasureSpec. V onMeasure se určí menší strana a nastaví se čtvercová velikost.

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

Vlastní ViewGroup s měřením podřízených prvků

ViewGroup vyžaduje složitější logiku onMeasure, protože nejprve se měří podřízené prvky, poté se určuje velikost samotného ViewGroup. Příklad CascadeLayout rozmísťuje podřízené prvky kaskádově s mezerou. Po změření všech podřízených prvků přes measureChildWithMargins se vypočítá celková šířka a výška.

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

Typické chyby při přepsání onMeasure

Vynechání volání setMeasuredDimension — nejčastější chyba. Pokud vývojář přepsal onMeasure, ale nezavolal setMeasuredDimension, aplikace spadne s IllegalStateException. K tomu dochází zváště často, když jsou v metodě podmíněné větvení a v jedné z větví volání chybí. Každá větev kódu v onMeasure musí končit voláním setMeasuredDimension.

Ignorování režimu AT_MOST — druhá nejčastější chyba. Pokud View v režimu AT_MOST vždy používá předanou velikost místo výpočtu na základě obsahu, rodičovský kontejner nebude moci správně rozdělit prostor. Například TextView v AT_MOST by měl vypočítat šířku textu a použít minimum z požadované a předané šířky. Ignorování AT_MOST vede k tomu, že View zabírá veškerý dostupný prostor i při malém obsahu.

Vytváření objektů uvnitř onMeasure — klasická chyba výkonu. Protože onMeasure může být voláno opakovaně (při každém požadavku na layout), vytváření objektů (Paint, Rect, String) uvnitř této metody znečišťuje paměť a vyvolává garbage collection. Všechny objekty by měly být vytvořeny jednou v konstruktoru View a v onMeasure by měla být prováděna pouze logika výpočtu rozměrů. Stejné pravidlo platí pro onDraw a onLayout.

Měření podřízených View ve ViewGroup

measureChildWithMargins — je protected metoda ViewGroup, která měří jedno podřízené View s ohledem na jeho MarginLayoutParams. Metoda přijímá MeasureSpec rodiče a nahromaděné posuny v šířce a výšce. Automaticky upravuje MeasureSpec pro podřízené View, odečítá paddingy rodiče a margin dítěte a předává upravený MeasureSpec do child.measure().

Pro pokročilou logiku měření může ViewGroup přepsat measureChild(View child, int parentWidthSpec, int parentHeightSpec) nebo pracovat přímo s MeasureSpec pro každý podřízený prvek. Například LinearLayout v onMeasure iteruje přes všechna podřízená View, měří každé s ohledem na jeho layout_weight a rozděluje zbývající prostor proporcionálně. Tento přístup umožňuje implementovat libovolné algoritmy rozvržení.

Cacheování výsledků měření pomocí mechanismu measure cache je k dispozici přes příznak setMeasureWithLargestChildEnabled v určitých ViewGroup. Ve většině případů je však onMeasure voláno znovu při každé změně layoutu a cacheování se neuplatňuje. Ve vlastních ViewGroup se doporučuje minimalizovat výpočty v onMeasure, nikoli spoléhat na cache.

Často kladené otázky

Je nutné přepsat onMeasure pro vlastní View?

Ano, pokud vlastní View dědí přímo z třídy View. Pokud dědění pochází z TextView, ImageView nebo Button s jejich standardními rozměry, onMeasure není třeba přepisovat. Pro ViewGroup je přepsání onMeasure vždy vyžadováno — jinak podřízené prvky nebudou správně změřeny.

Co se stane, když se nezavolá setMeasuredDimension?

Systém Android vyhazuje IllegalStateException s textem „The View did not call setMeasuredDimension”. Tato výjimka vzniká v metodě measure() po dokončení onMeasure, pokud konečné rozměry zůstaly nulové. Výjimka způsobí pád aplikace, pokud není ošetřena přes try-catch.

Čím se liší getWidth od getMeasuredWidth?

getMeasuredWidth() vrací velikost nastavenou v onMeasure (fáze měření). getWidth() vrací skutečnou velikost, kterou View obdrželo v onLayout po zohlednění všech korekcí umístění. Pro většinu View se tyto hodnoty shodují, ale ve vlastních ViewGroup se mohou lišit.

Lze v onMeasure měnit animace nebo stav View?

Ne, onMeasure je určeno výhradně pro výpočet rozměrů. Změna stavu, spouštění animací, práce se sítí nebo aktualizace dat v této metodě narušuje architekturu Android a může vést k rekurzivním voláním measure, protože změna stavu může spustit requestLayout.

Jak onMeasure interaguje s ConstraintLayout?

ConstraintLayout samostatně řídí měření podřízených prvků na základě daných omezení. Pokud vlastní View uvnitř ConstraintLayout přepisuje onMeasure, musí správně zpracovat MeasureSpec předané z ConstraintLayout, jinak omezení nemusí fungovat. ConstraintLayout používá dvouprůchodový algoritmus s vlastním WidgetContainer pro výpočet.

Shrnutí

  • onMeasure() — metoda View pro určení rozměrů, volaná systémem Android ve fázi measure
  • MeasureSpec kóduje režim měření (EXACTLY, AT_MOST, UNSPECIFIED) a velikost předanou od rodiče
  • setMeasuredDimension — povinné volání na konci onMeasure fixující konečné rozměry
  • resolveSize — pomocná metoda implementující standardní logiku zpracování MeasureSpec v jednom řádku
  • Dvouprůchodový algoritmus zaručuje správné měření v hierarchii rodič-podřízené prvky
  • measureChildWithMargins se používá pro měření podřízených View ve vlastních ViewGroup s ohledem na mezery
  • Vytváření objektů uvnitř onMeasure je kategoricky nedoporučováno kvůli riziku GC a výpadkům snímků

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é