onMeasure(): mi ez, a MeasureSpec módok és a metódus felülírása

Szerző: IT Sectr Megjelenés: 2026-07-22 Olvasási idő: 9 perc

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) — View metódus a méretek mérésére a MeasureSpec szülőtől való átadásával
  • MeasureSpec — 32 bites érték, amely kódolja a mérési módot (UNSPECIFIED, EXACTLY, AT_MOST) és a méretet
  • setMeasuredDimension(int w, int h) — kötelező hívás az onMeasure-n belül, amely rögzíti a View végső méreteit
  • Kétmenetes algoritmus mérése: a szülő megméri a gyerekeket, majd a gyerekek jelentik a méreteiket, és a szülő meghozza a végső döntést
  • measureChildWithMargins — segédmetódus a gyermek View-k méréséhez egyedi ViewGroup-okban

Mi az onMeasure()?

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 módok: három kulcsérték

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ékViselkedés
EXACTLYA szülő pontos méretet adott megA View-nak pontosan bele kell férnie az átadott méretbe, ha nem akar a határokon túllépni
AT_MOSTA szülő maximális méretet állított beA View bármilyen méretet választhat 0-tól az átadott maximumig
UNSPECIFIEDA szülő nem szab korlátozásokatA 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.

A MeasureSpec feldolgozásának tipikus logikája

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.

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

A resolveSize használata

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.

Kétmenetes mérési algoritmus

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.

MEASURED_SIZE_STATE zászló

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.

Példa az onMeasure felülírására Kotlinban

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.

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

Egyedi ViewGroup gyermek elemek mérésével

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.

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

Gyakori hibák az onMeasure felülírásakor

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.

Gyermek View-k mérése ViewGroup-ban

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

Kötelező felülírni az onMeasure-t egyedi View esetén?

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.

Mi történik, ha nem hívják meg a setMeasuredDimension-t?

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.

Mi a különbség a getWidth és a getMeasuredWidth között?

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.

Megváltoztathatók-e az animációk vagy a View állapota az onMeasure-ben?

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.

Hogyan működik együtt az onMeasure a ConstraintLayout-tal?

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ó

  • onMeasure() — View metódus a méretek meghatározására, amelyet az Android rendszer hív meg a measure fázisban
  • MeasureSpec kódolja a mérési módot (EXACTLY, AT_MOST, UNSPECIFIED) és a szülőtől átadott méretet
  • setMeasuredDimension — kötelező hívás az onMeasure végén, amely rögzíti a végső méreteket
  • resolveSize — segédmetódus, amely a MeasureSpec szabványos feldolgozási logikáját egy sorban implementálja
  • Kétmenetes algoritmus garantálja a helyes mérést a szülő-gyermek hierarchiában
  • measureChildWithMargins a gyermek View-k mérésére szolgál egyedi ViewGroup-okban a hézagok figyelembevételével
  • Objektumok létrehozása az onMeasure-n belül kategorikusan nem ajánlott a GC és a képkocka kiesés kockázata miatt

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.

Projekt megbeszélése

Olvassa el is