onMeasure(): ce este, modurile MeasureSpec și suprascrierea metodei

Autor: IT Sectr Publicat: 2026-07-22 Timp de citire: 9 min

onMeasure() — este o metodă protected a clasei android.view.View, care este apelată de sistemul Android pentru a determina dimensiunile View-ului. Sistemul transmite metodei două obiecte MeasureSpec, fiecare dintre ele conținând modul de măsurare (EXACTLY, AT_MOST sau UNSPECIFIED) și dimensiunea propusă de containerul părinte. Conform Android Developers Documentation (2026), suprascrierea onMeasure cu gestionarea corectă a MeasureSpec este o etapă obligatorie pentru toate View-urile și ViewGroup-urile personalizate care necesită control precis asupra dimensiunilor.

Principalele puncte

  • onMeasure(int widthMeasureSpec, int heightMeasureSpec) — metoda View pentru măsurarea dimensiunilor cu transmiterea MeasureSpec de la părinte
  • MeasureSpec — o valoare pe 32 de biți care codifică modul de măsurare (UNSPECIFIED, EXACTLY, AT_MOST) și dimensiunea
  • setMeasuredDimension(int w, int h) — apel obligatoriu în interiorul onMeasure, care fixează dimensiunile finale ale View-ului
  • Algoritmul în două treceri de măsurare: părintele măsoară copiii, apoi copiii raportează dimensiunile lor, iar părintele ia decizia finală
  • measureChildWithMargins — metodă auxiliară pentru măsurarea View-urilor copil în ViewGroup-uri personalizate

Ce este onMeasure()?

onMeasure(int widthMeasureSpec, int heightMeasureSpec) — este metoda clasei View pe care sistemul Android o apelează pentru a determina lățimea și înălțimea vizualizării. Dezvoltatorul suprascrie această metodă pentru a indica ce dimensiune ar trebui să aibă View-ul pe baza constrângerilor transmise în MeasureSpec. Fără o suprascriere corectă a onMeasure, View-ul personalizat se poate afișa incorect sau poate să nu se afișeze deloc.

Sistemul apelează onMeasure în faza measure a ciclului de viață al View-ului, care precede fazele layout (onLayout) și draw (onDraw). Dacă View-ul nu suprascrie onMeasure, se utilizează implementarea din superclasă, care stabilește dimensiunile implicite pe baza background drawable sau layout_params. Apelul super.onMeasure(widthMeasureSpec, heightMeasureSpec) funcționează doar pentru subclasele standard de View, cum ar fi TextView sau ImageView.

Cerința cheie pentru onMeasure — apelul setMeasuredDimension(int, int) trebuie să fie prezent obligatoriu la sfârșitul metodei. Dacă acest apel lipsește, sistemul generează excepția IllegalStateException cu mesajul că View-ul nu a fixat dimensiunile măsurate. Dimensiunile finale devin disponibile prin getter-ele getMeasuredWidth() și getMeasuredHeight() după finalizarea fazei measure.

Modurile MeasureSpec: trei valori cheie

MeasureSpec — este un număr întreg pe 32 de biți, în care cei 2 biți superiori codifică modul de măsurare, iar cei 30 de biți inferiori — dimensiunea. Modul determină cât de liber este View-ul în alegerea dimensiunii sale. Android oferă trei moduri: EXACTLY, AT_MOST și UNSPECIFIED. Fiecare mod dictează o logică diferită de procesare în onMeasure.

Mod MeasureSpecValoareComportament
EXACTLYPărintele a specificat dimensiunea exactăView-ul trebuie să se încadreze exact în dimensiunea transmisă, dacă nu vrea să iasă din limite
AT_MOSTPărintele a stabilit dimensiunea maximăView-ul poate alege orice dimensiune de la 0 până la maximul transmis
UNSPECIFIEDPărintele nu impune restricțiiView-ul poate alege orice dimensiune dorită fără limitări superioare

Pentru extragerea modului și dimensiunii din MeasureSpec se folosesc metodele statice ale clasei MeasureSpec: MeasureSpec.getMode(int) returnează unul dintre cele trei moduri (EXACTLY, AT_MOST, UNSPECIFIED), iar MeasureSpec.getSize(int) returnează dimensiunea numerică în pixeli. Pentru a crea propriul MeasureSpec se folosește MeasureSpec.makeMeasureSpec(int size, int mode). Aceste trei metode acoperă toate scenariile de lucru cu dimensiunile în onMeasure.

Logica tipică de procesare a MeasureSpec

Modelul standard de procesare a MeasureSpec: dacă modul este EXACTLY — utilizați dimensiunea transmisă ca finală; dacă este AT_MOST — alegeți minimul dintre dimensiunea dorită (conținutul View-ului) și maximul transmis; dacă este UNSPECIFIED — utilizați dimensiunea dorită a View-ului fără restricții. Acest model garantează un comportament corect sub orice constrângeri ale părintelui.

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

Utilizarea resolveSize

Pentru simplificarea logicii standard, Android oferă metoda resolveSizeAndState, care primește dimensiunea dorită, MeasureSpec și returnează dimensiunea finală cu modul corect. Această metodă implementează modelul descris mai sus într-o singură linie de cod. De asemenea, este disponibilă funcția resolveSize(int size, int measureSpec), care returnează dimensiunea pură fără biți de stare.

Algoritmul de măsurare în două treceri

Android utilizează un algoritm de măsurare în două treceri (two-pass measure), care garantează că fiecare View din ierarhie primește dimensiuni corecte ținând cont de constrângerile părintelui și preferințele elementelor copil. În prima trecere, părintele transmite View-urilor copil MeasureSpec cu constrângeri, iar View-urile copil își calculează dimensiunile dorite. În a doua trecere, părintele ia decizia finală privind dimensiunile.

Pentru ViewGroup, procesul de măsurare este mai complex: părintele trebuie mai întâi să măsoare toate elementele sale copil, apoi să își determine propria dimensiune pe baza dimensiunilor acestora. Apelul measureChildren(int widthMeasureSpec, int heightMeasureSpec) iterează prin toate View-urile copil și apelează pentru fiecare measure(child, childWidthSpec, childHeightSpec). După măsurarea tuturor elementelor copil, ViewGroup apelează setMeasuredDimension cu propriile dimensiuni.

Un aspect important: metoda measure (publică, final) nu poate fi suprascrisă — în locul ei se suprascrie onMeasure. Acest lucru garantează că sistemul poate executa acțiuni de serviciu înainte și după apelul onMeasure, cum ar fi verificarea modificării dimensiunilor și calculul zonei dirty pentru desenarea ulterioară. Dacă View-ul are dimensiuni fixe, suprascrierea onMeasure poate să nu fie necesară.

Steagul MEASURED_SIZE_STATE

MeasureSpec include nu doar dimensiunea și modul, ci și biți de stare (state bits), accesibili prin MeasureSpec.getMode(). După apelul setMeasuredDimension, starea devine parte a dimensiunilor măsurate ale View-ului și poate fi verificată prin getMeasuredState(). Acest lucru este utilizat în ScrollView și alte containere derulabile pentru transmiterea corectă a constrângerilor către copii.

Exemplu de suprascriere onMeasure în Kotlin

Să examinăm un exemplu practic de creare a unui View personalizat cu suprascrierea onMeasure pentru afișarea pătrată. Clasa SquareView moștenește View și garantează că lățimea și înălțimea sunt întotdeauna egale, indiferent de MeasureSpec-urile transmise. În onMeasure se determină latura mai mică și se stabilește dimensiunea pătrată.

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

ViewGroup personalizată cu măsurarea elementelor copil

ViewGroup necesită o logică onMeasure mai complexă, deoarece mai întâi se măsoară elementele copil, apoi se determină dimensiunea ViewGroup-ului însuși. Exemplul CascadeLayout distribuie elementele copil în cascadă cu spațiere. După măsurarea tuturor elementelor copil prin measureChildWithMargins, se calculează lățimea și înălțimea totală.

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

Greșeli tipice la suprascrierea onMeasure

Omisiunea apelului setMeasuredDimension — cea mai frecventă greșeală. Dacă dezvoltatorul a suprascris onMeasure dar nu a apelat setMeasuredDimension, aplicația se va prăbuși cu IllegalStateException. Acest lucru se întâmplă mai ales când în metodă există ramificări condiționale și într-una dintre ramuri apelul lipsește. Fiecare ramură de cod din onMeasure trebuie să se termine cu apelul setMeasuredDimension.

Ignorarea modului AT_MOST — a doua cea mai frecventă greșeală. Dacă View-ul în modul AT_MOST folosește întotdeauna dimensiunea transmisă în loc de calculul pe baza conținutului, containerul părinte nu va putea distribui corect spațiul. De exemplu, TextView în AT_MOST ar trebui să calculeze lățimea textului și să folosească minimul dintre lățimea dorită și cea transmisă. Ignorarea AT_MOST duce la ocuparea întregului spațiu disponibil de către View chiar și cu conținut mic.

Crearea de obiecte în interiorul onMeasure — o greșeală clasică de performanță. Deoarece onMeasure poate fi apelat de multiple ori (la fiecare cerere de layout), crearea de obiecte (Paint, Rect, String) în interiorul acestei metode aglomerează memoria și provoacă colectarea de gunoi. Toate obiectele trebuie create o singură dată în constructorul View-ului, iar în onMeasure trebuie executată doar logica de calcul a dimensiunilor. Aceeași regulă se aplică pentru onDraw și onLayout.

Măsurarea View-urilor copil în ViewGroup

measureChildWithMargins — este o metodă protected a ViewGroup care măsoară un View copil ținând cont de MarginLayoutParams-ul său. Metoda primește MeasureSpec-ul părintelui și deplasările acumulate pe lățime și înălțime. Ajustează automat MeasureSpec pentru View-ul copil, scăzând padding-urile părintelui și margin-ul copilului, și transmite MeasureSpec-ul ajustat către child.measure().

Pentru o logică de măsurare avansată, ViewGroup poate suprascrie measureChild(View child, int parentWidthSpec, int parentHeightSpec) sau poate lucra direct cu MeasureSpec pentru fiecare element copil. De exemplu, LinearLayout în onMeasure iterează prin toate View-urile copil, măsoară fiecare ținând cont de layout_weight-ul său și distribuie spațiul rămas proporțional. Această abordare permite implementarea unor algoritmi de layout arbitrari.

Cache-ul rezultatelor măsurătorii prin mecanismul de cache de măsurare (measure cache) este disponibil prin steagul setMeasureWithLargestChildEnabled în anumite ViewGroup-uri. Cu toate acestea, în majoritatea cazurilor onMeasure este apelat din nou la orice modificare de layout, iar cache-ul nu se aplică. În ViewGroup-urile personalizate se recomandă minimizarea calculelor în onMeasure, nu bazarea pe cache.

Întrebări frecvente

Este obligatorie suprascrierea onMeasure pentru un View personalizat?

Da, dacă View-ul personalizat moștenește direct din clasa View. Dacă moștenirea este de la TextView, ImageView sau Button cu dimensiunile lor standard, onMeasure poate să nu fie suprascris. Pentru ViewGroup, suprascrierea onMeasure este întotdeauna necesară — altfel elementele copil nu vor fi măsurate corect.

Ce se întâmplă dacă nu se apelează setMeasuredDimension?

Sistemul Android aruncă IllegalStateException cu textul „The View did not call setMeasuredDimension“. Această excepție apare în metoda measure() după finalizarea onMeasure, dacă dimensiunile finale au rămas zero. Excepția prăbușește aplicația dacă nu este gestionată prin try-catch.

Care este diferența dintre getWidth și getMeasuredWidth?

getMeasuredWidth() returnează dimensiunea stabilită în onMeasure (faza de măsurare). getWidth() returnează dimensiunea reală pe care View-ul a primit-o în onLayout după luarea în considerare a tuturor ajustărilor de poziționare. Pentru majoritatea View-urilor, aceste valori coincid, dar în ViewGroup-urile personalizate pot fi diferite.

Se pot modifica animațiile sau starea View-ului în onMeasure?

Nu, onMeasure este destinat exclusiv calculării dimensiunilor. Modificarea stării, pornirea animațiilor, lucrul cu rețeaua sau actualizarea datelor în această metodă încalcă arhitectura Android și poate duce la apeluri recursive measure, deoarece modificarea stării poate declanșa requestLayout.

Cum interacționează onMeasure cu ConstraintLayout?

ConstraintLayout gestionează independent măsurarea elementelor copil pe baza constrângerilor date. Dacă un View personalizat din interiorul ConstraintLayout suprascrie onMeasure, acesta trebuie să proceseze corect MeasureSpec-ul transmis de ConstraintLayout, altfel constrângerile pot să nu funcționeze. ConstraintLayout utilizează un algoritm în două treceri cu propriul WidgetContainer pentru calcul.

Rezumat

  • onMeasure() — metoda View pentru determinarea dimensiunilor, apelată de sistemul Android în faza measure
  • MeasureSpec codifică modul de măsurare (EXACTLY, AT_MOST, UNSPECIFIED) și dimensiunea transmisă de la părinte
  • setMeasuredDimension — apel obligatoriu la sfârșitul onMeasure, care fixează dimensiunile finale
  • resolveSize — metodă auxiliară care implementează logica standard de procesare a MeasureSpec într-o singură linie
  • Algoritmul în două treceri garantează măsurarea corectă în ierarhia părinte-elemente copil
  • measureChildWithMargins este utilizat pentru măsurarea View-urilor copil în ViewGroup-uri personalizate cu luarea în considerare a spațierilor
  • Crearea de obiecte în interiorul onMeasure este categoric nerecomandată din cauza riscului de GC și de pierdere a cadrelor

Vom dezvolta o aplicație mobilă la cheie

IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.

Discutați proiectul

Citiți și