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), 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, ü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 Modu | Değer | Davranış |
|---|---|---|
| EXACTLY | Üst tam bir boyut belirtti | View, sınırları aşmak istemiyorsa verilen boyuta tam olarak sığmalıdır |
| AT_MOST | Üst maksimum bir boyut belirledi | View, 0'dan verilen maksimuma kadar herhangi bir boyut seçebilir |
| UNSPECIFIED | Üst herhangi bir kısıtlama getirmez | View, ü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.
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.
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)
}
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.
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.
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.
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.
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, 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.
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
}
}
}
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.
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
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.
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.
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.
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.
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
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.
Ayrıca okuyun