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) — 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.
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 MeasureSpec | Hodnota | Chování |
|---|---|---|
| EXACTLY | Rodič určil přesnou velikost | View se musí přesně vejít do předané velikosti, pokud nechce překročit hranice |
| AT_MOST | Rodič nastavil maximální velikost | View může zvolit libovolnou velikost od 0 do předaného maxima |
| UNSPECIFIED | Rodič 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.
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.
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)
}
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ů.
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.
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.
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.
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)
}
}
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.
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
}
}
}
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.
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
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.
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.
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.
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.
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í
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í.
Přečtěte si také