onMeasure() — az android.view.View osztály protected metódusa, amelyet az Android rendszer hív meg a View méreteinek meghatározásához. A rendszer két MeasureSpec objektumot ad át a metódusnak, amelyek mindegyike tartalmazza a mérési módot (EXACTLY, AT_MOST vagy UNSPECIFIED) és a szülő konténer által javasolt méretet. Az Android Developers Documentation (2026) szerint az onMeasure felülírása a MeasureSpec helyes kezelésével kötelező lépés minden egyedi View és ViewGroup számára, amelyek pontos kontrollt igényelnek a méretek felett.
Főbb pontok
onMeasure(int widthMeasureSpec, int heightMeasureSpec) — a View osztály metódusa, amelyet az Android rendszer hív meg a nézet szélességének és magasságának meghatározásához. A fejlesztő felülírja ezt a metódust, hogy jelezze, mekkora méretűnek kell lennie a View-nak a MeasureSpec-ben átadott korlátozások alapján. Az onMeasure helyes felülírása nélkül az egyedi View helytelenül jelenhet meg, vagy egyáltalán nem jelenik meg.
A rendszer az onMeasure-t a View életciklusának measure fázisában hívja meg, amely megelőzi a layout (onLayout) és draw (onDraw) fázisokat. Ha a View nem írja felül az onMeasure-t, a szuperosztály implementációja kerül használatra, amely alapértelmezett méreteket állít be a background drawable vagy layout_params alapján. A super.onMeasure(widthMeasureSpec, heightMeasureSpec) hívás csak a View szabványos alosztályaihoz működik, mint a TextView vagy ImageView.
Az onMeasure legfontosabb követelménye — a setMeasuredDimension(int, int) hívásnak kötelezően jelen kell lennie a metódus végén. Ha ez a hívás hiányzik, a rendszer IllegalStateException kivételt generál azzal az üzenettel, hogy a View nem rögzítette a mért méreteket. A végső méretek a measure fázis befejezése után válnak elérhetővé a getMeasuredWidth() és getMeasuredHeight() getter-eken keresztül.
MeasureSpec — egy 32 bites egész szám, amelyben a felső 2 bit kódolja a mérési módot, az alsó 30 bit pedig a méretet. A mód meghatározza, mennyire szabad a View a méretének kiválasztásában. Az Android három módot kínál: EXACTLY, AT_MOST és UNSPECIFIED. Mindegyik mód eltérő feldolgozási logikát diktál az onMeasure-ben.
| MeasureSpec mód | Érték | Viselkedés |
|---|---|---|
| EXACTLY | A szülő pontos méretet adott meg | A View-nak pontosan bele kell férnie az átadott méretbe, ha nem akar a határokon túllépni |
| AT_MOST | A szülő maximális méretet állított be | A View bármilyen méretet választhat 0-tól az átadott maximumig |
| UNSPECIFIED | A szülő nem szab korlátozásokat | A View bármilyen kívánt méretet választhat felső korlátozások nélkül |
A mód és méret kinyeréséhez a MeasureSpec-ből a MeasureSpec osztály statikus metódusait használjuk: a MeasureSpec.getMode(int) visszaadja a három mód egyikét (EXACTLY, AT_MOST, UNSPECIFIED), a MeasureSpec.getSize(int) pedig a numerikus méretet pixelekben. Saját MeasureSpec létrehozásához a MeasureSpec.makeMeasureSpec(int size, int mode) használható. Ez a három metódus lefedi az összes méretekkel kapcsolatos forgatókönyvet az onMeasure-ben.
Szabványos minta a MeasureSpec feldolgozásához: ha a mód EXACTLY — használja az átadott méretet véglegesként; ha AT_MOST — válassza a minimálisat a kívánt méret (View tartalma) és az átadott maximum közül; ha UNSPECIFIED — használja a View kívánt méretét korlátozások nélkül. Ez a minta garantálja a helyes viselkedést a szülő bármilyen korlátozása mellett.
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)
}
A szabványos logika egyszerűsítésére az Android resolveSizeAndState metódust kínál, amely elfogadja a kívánt méretet, MeasureSpec-et, és visszaadja a végső méretet a helyes móddal. Ez a metódus a fent leírt mintát egyetlen kódsorban implementálja. Elérhető továbbá a resolveSize(int size, int measureSpec) függvény, amely a tiszta méretet adja vissza állapotbitek nélkül.
Android egy kétmenetes mérési algoritmust (two-pass measure) használ, amely garantálja, hogy a hierarchia minden View-ja helyes méreteket kap, figyelembe véve a szülő korlátozásait és a gyermek elemek preferenciáit. Az első menetben a szülő MeasureSpec-et ad át korlátozásokkal a gyermek View-knak, a gyermek View-k pedig kiszámítják a kívánt méreteiket. A második menetben a szülő meghozza a végső döntést a méretekről.
ViewGroup esetén a mérési folyamat összetettebb: a szülőnek először meg kell mérnie az összes gyermek elemét, majd meg kell határoznia saját méretét azok méretei alapján. A measureChildren(int widthMeasureSpec, int heightMeasureSpec) hívás végigiterál az összes gyermek View-n, és mindegyikre meghívja a measure(child, childWidthSpec, childHeightSpec)-t. Az összes gyermek elem megmérése után a ViewGroup meghívja a setMeasuredDimension-t a saját méreteivel.
Fontos árnyalat: a measure metódus (publikus, final) nem írható felül — helyette az onMeasure írható felül. Ez garantálja, hogy a rendszer szolgáltatási műveleteket hajthat végre az onMeasure hívása előtt és után, például a méretváltozás ellenőrzését és a piszkos terület kiszámítását a későbbi rajzoláshoz. Ha a View-nak rögzített méretei vannak, az onMeasure felülírása nem szükséges.
MeasureSpec nemcsak a méretet és a módot tartalmazza, hanem állapotbiteket (state bits) is, amelyek a MeasureSpec.getMode()-on keresztül érhetők el. A setMeasuredDimension hívása után az állapot a View mért méreteinek részévé válik, és a getMeasuredState()-on keresztül ellenőrizhető. Ez a ScrollView-ban és más görgethető konténerekben használatos a korlátozások helyes átadásához a gyerekeknek.
Nézzünk meg egy gyakorlati példát egy egyedi View létrehozására az onMeasure felülírásával négyzetes megjelenítéshez. A SquareView osztály örökli a View-t, és garantálja, hogy a szélesség és magasság mindig egyenlő, függetlenül az átadott MeasureSpec-ektől. Az onMeasure-ben meghatározzuk a kisebb oldalt, és beállítjuk a négyzetes méretet.
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 összetettebb onMeasure logikát igényel, mivel először a gyermek elemeket mérik, majd a ViewGroup saját méretét határozzák meg. A CascadeLayout példa a gyermek elemeket kaszkádban helyezi el hézagokkal. Az összes gyermek elem measureChildWithMargins-on keresztüli megmérése után kiszámításra kerül a teljes szélesség és magasság.
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
}
}
}
A setMeasuredDimension hívás kihagyása — a leggyakoribb hiba. Ha a fejlesztő felülírta az onMeasure-t, de nem hívta meg a setMeasuredDimension-t, az alkalmazás IllegalStateException-nel összeomlik. Ez különösen gyakran fordul elő, ha a metódusban feltételes elágazások vannak, és az egyik ágból hiányzik a hívás. Az onMeasure minden kódágának a setMeasuredDimension hívással kell végződnie.
Az AT_MOST mód figyelmen kívül hagyása — a második leggyakoribb hiba. Ha a View AT_MOST módban mindig az átadott méretet használja a tartalom alapján történő számítás helyett, a szülő konténer nem tudja helyesen elosztani a helyet. Például egy TextView-nak AT_MOST módban ki kell számítania a szöveg szélességét, és a kívánt és átadott szélesség minimumát kell használnia. Az AT_MOST figyelmen kívül hagyása azt eredményezi, hogy a View az összes rendelkezésre álló helyet elfoglalja, még kis tartalom esetén is.
Objektumok létrehozása az onMeasure-n belül — klasszikus teljesítményhiba. Mivel az onMeasure többször is meghívható (minden layout kérésnél), objektumok (Paint, Rect, String) létrehozása ezen a metóduson belül szennyezi a memóriát és szemetgyűjtést vált ki. Az összes objektumot egyszer kell létrehozni a View konstruktorában, és az onMeasure-ben csak a méretszámítás logikáját szabad végrehajtani. Ugyanez a szabály vonatkozik az onDraw és onLayout metódusokra is.
measureChildWithMargins — a ViewGroup protected metódusa, amely egy gyermek View-t mér meg a MarginLayoutParams figyelembevételével. A metódus elfogadja a szülő MeasureSpec-jét és a felhalmozott eltolásokat szélességben és magasságban. Automatikusan korrigálja a MeasureSpec-et a gyermek View számára, kivonva a szülő padding-jét és a gyermek margin-jét, és továbbítja a korrigált MeasureSpec-et a child.measure()-nak.
Fejlett mérési logikához a ViewGroup felülírhatja a measureChild(View child, int parentWidthSpec, int parentHeightSpec)-t, vagy közvetlenül dolgozhat a MeasureSpec-szel minden gyermek elemhez. Például a LinearLayout az onMeasure-ben végigiterál az összes gyermek View-n, mindegyiket a layout_weight figyelembevételével méri meg, és a fennmaradó helyet arányosan osztja el. Ez a megközelítés lehetővé teszi tetszőleges elrendezési algoritmusok implementálását.
A mérési eredmények gyorsítótárazása a mérési gyorsítótár (measure cache) mechanizmusán keresztül a setMeasureWithLargestChildEnabled zászlón keresztül érhető el bizonyos ViewGroup-okban. A legtöbb esetben azonban az onMeasure minden layout változásnál újra meghívódik, és a gyorsítótárazás nem alkalmazandó. Egyedi ViewGroup-okban ajánlott minimalizálni a számításokat az onMeasure-ben, nem pedig a gyorsítótárra hagyatkozni.
Gyakran Ismételt Kérdések
Igen, ha az egyedi View közvetlenül a View osztályból örököl. Ha az öröklés TextView, ImageView vagy Button osztályból történik a szabványos méreteikkel, az onMeasure-t nem kell felülírni. ViewGroup esetén az onMeasure felülírása mindig szükséges — különben a gyermek elemek nem lesznek helyesen megmérve.
Az Android rendszer IllegalStateException kivételt dob a „The View did not call setMeasuredDimension” szöveggel. Ez a kivétel a measure() metódusban keletkezik az onMeasure befejezése után, ha a végső méretek nullák maradtak. A kivétel összeomlasztja az alkalmazást, ha nem kezelik try-catch segítségével.
getMeasuredWidth() az onMeasure-ben beállított méretet adja vissza (mérési fázis). getWidth() a tényleges méretet adja vissza, amelyet a View az onLayout-ban kapott az összes pozícionálási korrekció figyelembevétele után. A legtöbb View esetén ezek az értékek megegyeznek, de egyedi ViewGroup-okban eltérhetnek.
Nem, az onMeasure kizárólag a méretek kiszámítására szolgál. Az állapot megváltoztatása, animációk indítása, hálózati munka vagy adatok frissítése ebben a metódusban megsérti az Android architektúrát, és rekurzív measure hívásokhoz vezethet, mivel az állapotváltozás requestLayout-ot válthat ki.
ConstraintLayout önállóan kezeli a gyermek elemek mérését a megadott korlátozások alapján. Ha egy egyedi View a ConstraintLayout-on belül felülírja az onMeasure-t, helyesen kell feldolgoznia a ConstraintLayout-tól átadott MeasureSpec-et, különben a korlátozások nem működhetnek. A ConstraintLayout kétmenetes algoritmust használ saját WidgetContainer-ével a számításhoz.
Összefoglaló
Kulcsrakész mobilalkalmazást fejlesztünk
Az IT Sectr 2017 óta készít iOS és Android alkalmazásokat induló vállalkozásoknak és vállalkozásoknak. Tanácsot adunk, és a legjobb megoldást javasoljuk.
Olvassa el is