onMeasure(): nedir, MeasureSpec modları ve metot geçersiz kılma

Yazar: IT Sectr Yayınlanma: 2026-07-22 Okuma süresi: 9 dk

onMeasure(), android.view.View sınıfının protected bir metodu olup Android sistemi tarafından bir View'in boyutlarını belirlemek için çağrılır. Sistem metoda iki MeasureSpec nesnesi gönderir; her biri bir ölçüm modu (EXACTLY, AT_MOST veya UNSPECIFIED) ve üst kapsayıcı tarafından önerilen bir boyut içerir. Android Developers Documentation (2026)'ya göre, hassas boyut kontrolü gerektiren tüm özel View ve ViewGroup'lar için MeasureSpec'in doğru şekilde işlenmesiyle onMeasure'yi geçersiz kılmak zorunlu bir adımdır.

Önemli Noktalar

  • onMeasure(int widthMeasureSpec, int heightMeasureSpec) — üstten MeasureSpec alarak boyutları ölçen View metodu
  • MeasureSpec — ölçüm modunu (UNSPECIFIED, EXACTLY, AT_MOST) ve boyutu kodlayan 32 bitlik bir değer
  • setMeasuredDimension(int w, int h) — onMeasure içinde View'in son boyutlarını belirleyen zorunlu çağrı
  • İki geçişli algoritma ölçüm için: üst öğeleri ölçer, ardından öğeler boyutlarını bildirir ve üst son kararı verir
  • measureChildWithMargins — özel ViewGroup'larda alt View'ları ölçmek için yardımcı metot

onMeasure() nedir?

onMeasure(int widthMeasureSpec, int heightMeasureSpec), View sınıfının Android sistemi tarafından bir görünümün genişlik ve yüksekliğini belirlemek için çağrılan bir metottur. Geliştirici, MeasureSpec'te iletilen kısıtlamalara göre View'in hangi boyutta olması gerektiğini belirtmek için bu metodu geçersiz kılar. Doğru onMeasure geçersiz kılma olmadan, özel bir View yanlış görüntülenebilir veya hiç görünmeyebilir.

Sistem, View yaşam döngüsünün measure aşaması sırasında onMeasure'yi çağırır. Bu aşama, layout (onLayout) ve draw (onDraw) aşamalarından önce gelir. Bir View onMeasure'yi geçersiz kılmazsa, süper sınıfın uygulaması kullanılır ve background drawable veya layout_params temelinde varsayılan boyutlar ayarlanır. super.onMeasure(widthMeasureSpec, heightMeasureSpec) çağrısı yalnızca TextView veya ImageView gibi standart View alt sınıfları için çalışır.

onMeasure için önemli bir gereklilik, setMeasuredDimension(int, int) çağrısının metodun sonunda bulunması gerektiğidir. Bu çağrı eksikse, sistem View'in ölçülen boyutları ayarlamadığını belirten bir IllegalStateException fırlatır. Nihai boyutlar, measure aşaması tamamlandıktan sonra getMeasuredWidth() ve getMeasuredHeight() getter'ları aracılığıyla kullanılabilir hale gelir.

MeasureSpec Modları: Üç Temel Değer

MeasureSpec, üst 2 bitin ölçüm modunu ve alt 30 bitin boyutu kodladığı 32 bitlik bir tamsayıdır. Mod, View'in kendi boyutunu seçme özgürlüğünü belirler. Android üç mod sağlar: EXACTLY, AT_MOST ve UNSPECIFIED. Her mod, onMeasure'de farklı işleme mantığı belirler.

MeasureSpec ModuDeğerDavranış
EXACTLYÜst tam bir boyut belirttiView, sınırları aşmak istemiyorsa verilen boyuta tam olarak sığmalıdır
AT_MOSTÜst maksimum bir boyut belirlediView, 0'dan verilen maksimuma kadar herhangi bir boyut seçebilir
UNSPECIFIEDÜst herhangi bir kısıtlama getirmezView, üst sınır olmadan istediği herhangi bir boyutu seçebilir

MeasureSpec'ten modu ve boyutu çıkarmak için MeasureSpec sınıfının statik metotları kullanılır: MeasureSpec.getMode(int) üç moddan birini (EXACTLY, AT_MOST, UNSPECIFIED) döndürür ve MeasureSpec.getSize(int) piksel cinsinden sayısal boyutu döndürür. Özel bir MeasureSpec oluşturmak için MeasureSpec.makeMeasureSpec(int size, int mode) kullanılır. Bu üç metot, onMeasure'de boyutlarla çalışmanın tüm senaryolarını kapsar.

Tipik MeasureSpec İşleme Mantığı

Standart desen MeasureSpec işleme için: mod EXACTLY ise — iletilen boyutu son boyut olarak kullanın; AT_MOST ise — istenen boyut (View'in içeriği) ile iletilen maksimum arasındaki minimumu seçin; UNSPECIFIED ise — kısıtlama olmadan View'in istenen boyutunu kullanın. Bu desen, üst kısıtlamaları altında doğru davranışı garanti eder.

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

resolveSize Kullanımı

Standart mantığı basitleştirmek için Android, istenen boyutu ve MeasureSpec'i alıp doğru modla nihai boyutu döndüren resolveSizeAndState metodu sağlar. Bu metot, yukarıda açıklanan deseni tek bir kod satırında uygular. Durum bitleri olmadan temiz bir boyut döndüren resolveSize(int size, int measureSpec) işlevi de mevcuttur.

İki Geçişli Ölçüm Algoritması

Android, hiyerarşideki her View'in üst kısıtlamaları ve alt tercihleri dikkate alarak doğru boyutları almasını sağlayan iki geçişli bir ölçüm algoritması kullanır. İlk geçişte, üst, alt View'lara kısıtlamalarla MeasureSpec gönderir ve alt View'lar istenen boyutlarını hesaplar. İkinci geçişte, üst boyutlar hakkında nihai kararı verir.

ViewGroup için ölçüm süreci daha karmaşıktır: üst önce tüm alt öğelerini ölçmeli, ardından onların boyutlarına göre kendi boyutunu belirlemelidir. measureChildren(int widthMeasureSpec, int heightMeasureSpec) çağrısı tüm alt View'ları dolaşır ve her biri için measure(child, childWidthSpec, childHeightSpec) çağrısını yapar. Tüm alt öğeleri ölçtükten sonra ViewGroup, kendi boyutlarıyla setMeasuredDimension çağrısını yapar.

Önemli bir nüans: measure metodu (public, final) geçersiz kılınamaz — bunun yerine onMeasure geçersiz kılınır. Bu, sistemin onMeasure'den önce ve sonra, boyut değişikliklerini kontrol etme ve sonraki çizim için kirli alanı hesaplama gibi bakım görevlerini gerçekleştirebilmesini sağlar. Bir View'in sabit boyutları varsa, onMeasure'yi geçersiz kılmak gerekmeyebilir.

MEASURED_SIZE_STATE Bayrağı

MeasureSpec yalnızca boyut ve modu değil, aynı zamanda MeasureSpec.getMode() aracılığıyla erişilebilen durum bitlerini de içerir. setMeasuredDimension çağrıldıktan sonra durum, View'in ölçülen boyutlarının bir parçası haline gelir ve getMeasuredState() aracılığıyla kontrol edilebilir. Bu, ScrollView ve diğer kaydırılabilir kaplarda kısıtlamaları alt öğelere doğru şekilde iletmek için kullanılır.

Kotlin'de onMeasure Geçersiz Kılma Örneği

Kare görüntüleme için onMeasure geçersiz kılınmış özel bir View oluşturmanın pratik bir örneğini inceleyelim. SquareView sınıfı, View'i genişletir ve iletilen MeasureSpec'ten bağımsız olarak genişlik ve yüksekliğin her zaman eşit olmasını sağlar. onMeasure'de minimum kenar belirlenir ve kare boyut ayarlanır.

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

Alt Ölçümüyle Özel ViewGroup

ViewGroup, daha karmaşık onMeasure mantığı gerektirir çünkü önce alt öğeler ölçülmeli, ardından ViewGroup'un kendi boyutu belirlenmelidir. CascadeLayout örneği, alt öğeleri bir ofsetle kaskad şeklinde dağıtır. measureChildWithMargins aracılığıyla tüm alt öğeler ölçüldükten sonra toplam genişlik ve yükseklik hesaplanır.

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

onMeasure Geçersiz Kılarken Sık Yapılan Hatalar

setMeasuredDimension çağrısının unutulması en yaygın hatadır. Bir geliştirici onMeasure'yi geçersiz kılar ancak setMeasuredDimension'u çağırmazsa, uygulama IllegalStateException ile çöker. Bu özellikle metotta koşullu dallar olduğunda ve dallardan birinde çağrı eksik olduğunda meydana gelir. onMeasure'deki her kod dalı, setMeasuredDimension çağrısıyla bitmelidir.

AT_MOST modunu görmezden gelmek ikinci en yaygın hatadır. AT_MOST modundaki bir View, içeriğe göre hesaplama yapmak yerine her zaman iletilen boyutu kullanırsa, üst kapsayıcı alanı doğru şekilde dağıtamaz. Örneğin, AT_MOST modundaki bir TextView, metin genişliğini hesaplamalı ve istenen genişlik ile iletilen genişlik arasındaki minimumu kullanmalıdır. AT_MOST'u görmezden gelmek, View'in küçük içerikle bile mevcut tüm alanı kaplamasına neden olur.

onMeasure içinde nesne oluşturmak klasik bir performans hatasıdır. onMeasure birden çok kez çağrılabileceğinden (her layout isteğinde), bu metot içinde nesneler (Paint, Rect, String) oluşturmak belleği tıkar ve garbage collection'ı tetikler. Tüm nesneler View yapıcısında bir kez oluşturulmalı ve onMeasure'de yalnızca boyut hesaplama mantığı çalıştırılmalıdır. Aynı kural onDraw ve onLayout için de geçerlidir.

ViewGroup'da Alt View'ları Ölçme

measureChildWithMargins, ViewGroup'un korumalı bir metodu olup, MarginLayoutParams'ını dikkate alarak tek bir alt View'i ölçer. Metot, üst MeasureSpec'i ve birikmiş genişlik ve yükseklik ofsetlerini kabul eder. Üst padding'lerini ve alt margin'lerini çıkararak alt View için MeasureSpec'i otomatik olarak ayarlar ve ardından ayarlanmış MeasureSpec'i child.measure()'a gönderir.

Gelişmiş ölçüm mantığı için ViewGroup, measureChild(View child, int parentWidthSpec, int parentHeightSpec) metodu geçersiz kılabilir veya her alt öğe için doğrudan MeasureSpec ile çalışabilir. Örneğin, LinearLayout onMeasure'de tüm alt View'ları dolaşır, her birini layout_weight'ini dikkate alarak ölçer ve kalan alanı oransal olarak dağıtır. Bu yaklaşım, rastgele layout algoritmalarını uygulamaya izin verir.

Ölçüm sonuçlarını ölçüm önbelleği mekanizması aracılığıyla önbelleğe almak, belirli ViewGroup'larda setMeasureWithLargestChildEnabled bayrağı aracılığıyla kullanılabilir. Ancak çoğu durumda onMeasure, her layout değişikliğinde yeniden çağrılır ve önbellek uygulanmaz. Özel ViewGroup'larda, önbelleğe güvenmek yerine onMeasure'deki hesaplamaları en aza indirmek önerilir.

Sıkça Sorulan Sorular

Özel bir View için onMeasure'yi geçersiz kılmak zorunlu mu?

Evet, özel View doğrudan View sınıfından miras alıyorsa. TextView, ImageView veya Button'dan standart boyutlarıyla miras alıyorsa, onMeasure değiştirilmeden bırakılabilir. ViewGroup için, onMeasure'yi geçersiz kılmak her zaman gereklidir — aksi takdirde alt öğeler doğru şekilde ölçülmez.

setMeasuredDimension çağrılmazsa ne olur?

Android sistemi, “The View did not call setMeasuredDimension” mesajıyla IllegalStateException fırlatır. Bu istisna, onMeasure tamamlandıktan sonra measure() metodunda, nihai boyutlar sıfır kalmışsa meydana gelir. try-catch ile işlenmezse istisna uygulamayı çökertebilir.

getWidth ve getMeasuredWidth arasındaki fark nedir?

getMeasuredWidth(), onMeasure'de (ölçüm aşaması) ayarlanan boyutu döndürür. getWidth(), View'in onLayout'da tüm konum ayarlamalarından sonra aldığı gerçek boyutu döndürür. Çoğu View için bu değerler eşleşir, ancak özel ViewGroup'larda farklılık gösterebilir.

onMeasure içinde animasyonlar veya View durumu değiştirilebilir mi?

Hayır, onMeasure yalnızca boyut hesaplaması içindir. Bu metotta durum değiştirme, animasyon başlatma, ağ çalışması yapma veya veri güncelleme, Android mimarisini ihlal eder ve yinelemeli measure çağrılarına yol açabilir, çünkü durum değişiklikleri requestLayout'u tetikleyebilir.

onMeasure, ConstraintLayout ile nasıl etkileşir?

ConstraintLayout, tanımlanan kısıtlamalara göre alt ölçümünü bağımsız olarak yönetir. ConstraintLayout içindeki özel bir View onMeasure'yi geçersiz kılarsa, ConstraintLayout'tan iletilen MeasureSpec'i doğru şekilde işlemelidir, aksi takdirde kısıtlamalar çalışmayabilir. ConstraintLayout, hesaplama için kendi WidgetContainer'ı ile iki geçişli bir algoritma kullanır.

Özet

  • onMeasure() — ölçüm aşamasında Android sistemi tarafından çağrılan, boyutları belirleyen View metodu
  • MeasureSpec, ölçüm modunu (EXACTLY, AT_MOST, UNSPECIFIED) ve üstten iletilen boyutu kodlar
  • setMeasuredDimension — onMeasure sonunda nihai boyutları ayarlayan zorunlu çağrı
  • resolveSize — standart MeasureSpec işleme mantığını tek satırda uygulayan yardımcı metot
  • İki geçişli algoritma, üst-alt hiyerarşisinde doğru ölçümü garanti eder
  • measureChildWithMargins, özel ViewGroup'larda kenar boşluklarıyla alt View'ları ölçmek için kullanılır
  • GC ve kare düşmesi riski nedeniyle onMeasure içinde nesne oluşturmak kesinlikle önerilmez

Anahtar teslim bir mobil uygulama geliştireceğiz

IT Sectr, 2017'den beri girişimler ve işletmeler için iOS ve Android uygulamaları oluşturmaktadır. Size danışmanlık yapacak ve en iyi çözümü önereceğiz.

Projeyi tartış

Ayrıca okuyun