onMeasure() — android.view.View sinfinin qorunan metodudur və sistem Android tərəfindən View-in ölçülərini təyin etmək üçün çağırılır. Sistem metoda iki MeasureSpec obyekti ötürür, onların hər biri ölçmə rejimini (EXACTLY, AT_MOST və ya UNSPECIFIED) və valideyn konteyner tərəfindən təklif olunan ölçünü ehtiva edir. Android Developers Documentation (2026) məlumatına görə, MeasureSpec-in düzgün işlənməsi ilə onMeasure-in yenidən təyin edilməsi ölçülər üzərində dəqiq nəzarət tələb edən bütün xüsusi View və ViewGroup-lar üçün məcburi mərhələdir.
Əsas məqamlar
onMeasure(int widthMeasureSpec, int heightMeasureSpec) — View sinfinin metodudur, sistem Android tərəfindən görünüşün enini və hündürlüyünü təyin etmək üçün çağırılır. Tərtibatçı bu metodu yenidən təyin edərək, MeasureSpec-də ötürülən məhdudiyyətlər əsasında View-in hansı ölçüdə olması lazım olduğunu göstərir. onMeasure-in düzgün yenidən təyin edilməsi olmadan xüsusi View səhv göstərilə bilər və ya ümumiyyətlə göstərilməyə bilər.
Sistem onMeasure-i View-in həyat dövrünün measure mərhələsində çağırır, bu mərhələ layout (onLayout) və draw (onDraw) mərhələlərindən əvvəl gəlir. Əgər View onMeasure-i yenidən təyin etmirsə, background drawable və ya layout_params əsasında standart ölçüləri təyin edən superklassın implementasiyası istifadə olunur. super.onMeasure(widthMeasureSpec, heightMeasureSpec) çağırışı yalnız TextView və ya ImageView kimi standart View alt sinifləri üçün işləyir.
onMeasure üçün əsas tələb setMeasuredDimension(int, int) çağırışının metodun sonunda mütləq mövcud olmasıdır. Əgər bu çağırış yoxdursa, sistem View-in ölçülmüş ölçüləri təsbit etmədiyi barədə mesajla IllegalStateException istisnası yaradır. Yekun ölçülər measure mərhələsi başa çatdıqdan sonra getMeasuredWidth() və getMeasuredHeight() getter-ləri vasitəsilə əlçatan olur.
MeasureSpec — 32-bitlik tam ədəddir, burada yuxarı 2 bit ölçmə rejimini, aşağı 30 bit isə ölçünü kodlayır. Rejim View-in öz ölçüsünü seçməkdə nə dərəcədə sərbəst olduğunu müəyyən edir. Android üç rejim təqdim edir: EXACTLY, AT_MOST və UNSPECIFIED. Hər bir rejim onMeasure-də fərqli emal məntiqini diktə edir.
| MeasureSpec rejimi | Dəyər | Davranış |
|---|---|---|
| EXACTLY | Valideyn dəqiq ölçünü təyin etdi | View sərhədləri aşmaq istəmirsə, ötürülən ölçüyə tam uyğunlaşmalıdır |
| AT_MOST | Valideyn maksimum ölçünü təyin etdi | View 0-dan ötürülən maksimuma qədər istənilən ölçünü seçə bilər |
| UNSPECIFIED | Valideyn məhdudiyyət qoymur | View yuxarı məhdudiyyət olmadan istənilən arzu olunan ölçünü seçə bilər |
MeasureSpec-dən rejim və ölçünü çıxarmaq üçün MeasureSpec sinfinin statik metodları istifadə olunur: MeasureSpec.getMode(int) üç rejimdən birini qaytarır (EXACTLY, AT_MOST, UNSPECIFIED), MeasureSpec.getSize(int) isə pikseldə rəqəmsal ölçünü qaytarır. Öz MeasureSpec-inizi yaratmaq üçün MeasureSpec.makeMeasureSpec(int size, int mode) istifadə olunur. Bu üç metod onMeasure-də ölçülərlə işin bütün ssenarilərini əhatə edir.
Standart nümunə MeasureSpec emalı: əgər rejim EXACTLY-dirsə — ötürülən ölçünü yekun olaraq istifadə edin; əgər AT_MOST-dursa — arzu olunan ölçüdən (View-in məzmunu) və ötürülən maksimumdan minimumu seçin; əgər UNSPECIFIED-dirsə — məhdudiyyət olmadan View-in arzu olunan ölçüsünü istifadə edin. Bu nümunə valideynin istənilən məhdudiyyətlərində düzgün davranışı təmin edir.
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 məntiqi sadələşdirmək üçün Android resolveSizeAndState metodunu təqdim edir, bu metod arzu olunan ölçünü, MeasureSpec-i qəbul edir və düzgün rejimlə yekun ölçünü qaytarır. Bu metod yuxarıda təsvir olunan nümunəni bir sətir kodda həyata keçirir. Həmçinin resolveSize(int size, int measureSpec) funksiyası mövcuddur, dövlət bitləri olmadan təmiz ölçünü qaytarır.
Android iki prokedli ölçmə alqoritmindən (two-pass measure) istifadə edir ki, bu da iyerarxiyada hər bir View-in valideyn məhdudiyyətləri və uşaq elementlərinin üstünlükləri nəzərə alınmaqla düzgün ölçülər almasını təmin edir. Birinci prokeddə valideyn uşaq View-lərə məhdudiyyətlərlə MeasureSpec ötürür, uşaq View-lər isə öz arzu olunan ölçülərini hesablayır. İkinci prokeddə valideyn ölçülər haqqında qəti qərar verir.
ViewGroup üçün ölçmə prosesi daha mürəkkəbdir: valideyn əvvəlcə bütün uşaq elementlərini ölçməli, sonra onların ölçüləri əsasında öz ölçüsünü təyin etməlidir. measureChildren(int widthMeasureSpec, int heightMeasureSpec) çağırışı bütün uşaq View-lər arasında dövr edir və hər biri üçün measure(child, childWidthSpec, childHeightSpec) çağırır. Bütün uşaq elementləri ölçüldükdən sonra ViewGroup öz ölçüləri ilə setMeasuredDimension çağırır.
Əhəmiyyətli nüans: measure metodu (public, final) yenidən təyin edilə bilməz — onun əvəzinə onMeasure yenidən təyin edilir. Bu, sistemin onMeasure çağırışından əvvəl və sonra xidməti hərəkətləri yerinə yetirə bilməsini təmin edir, məsələn, ölçü dəyişikliyini yoxlamaq və sonrakı çəkmə üçün çirkli sahəni hesablamaq. Əgər View sabit ölçülərə malikdirsə, onMeasure-in yenidən təyin edilməsi tələb olunmaya bilər.
MeasureSpec təkcə ölçü və rejimi deyil, həm də MeasureSpec.getMode() vasitəsilə əldə edilə bilən dövlət bitlərini (state bits) ehtiva edir. setMeasuredDimension çağırışından sonra dövlət View-in ölçülmüş ölçülərinin bir hissəsinə çevrilir və getMeasuredState() vasitəsilə yoxlanıla bilər. Bu, ScrollView və digər sürüşdürülə bilən konteynerlərdə uşaqlara məhdudiyyətlərin düzgün ötürülməsi üçün istifadə olunur.
Praktiki nümunəni nəzərdən keçirək — kvadrat göstərmək üçün onMeasure-in yenidən təyin edilməsi ilə xüsusi View yaradılması. SquareView sinfi View-dən miras alır və ötürülən MeasureSpec-lərdən asılı olmayaraq en və hündürlüyün həmişə bərabər olmasını təmin edir. onMeasure-də kiçik tərəf müəyyən edilir və kvadrat ölçü təyin olunur.
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 mürəkkəb onMeasure məntiqi tələb edir, çünki əvvəlcə uşaq elementləri ölçülür, sonra ViewGroup-un öz ölçüsü müəyyən edilir. CascadeLayout nümunəsi uşaq elementlərini fasilə ilə kaskad şəklində yerləşdirir. measureChildWithMargins vasitəsilə bütün uşaq elementləri ölçüldükdən sonra ümumi en və hündürlük hesablanı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ışının buraxılması — ən çox yayılmış səhvdir. Əgər tərtibatçı onMeasure-i yenidən təyin edib, lakin setMeasuredDimension çağırmayıbsa, tətbiq IllegalStateException ilə çökəcək. Bu xüsusilə metoddə şərtli budaqlanmalar olduqda və budaqlardan birində çağırış olmadıqda tez-tez baş verir. onMeasure-də kodun hər bir budağı setMeasuredDimension çağırışı ilə bitməlidir.
AT_MOST rejiminə məhəl qoymamaq — ikinci ən çox rast gəlinən səhvdir. Əgər View AT_MOST rejimində məzmuna görə hesablama əvəzinə həmişə ötürülən ölçüdən istifadə edirsə, valideyn konteyner məkanı düzgün bölüşdürə bilməyəcək. Məsələn, AT_MOST-da TextView mətnin enini hesablamalı və arzu olunan və ötürülən endən minimumu istifadə etməlidir. AT_MOST-a məhəl qoymamaq View-in kiçik məzmunda belə bütün mövcud məkanı tutmasına səbəb olur.
onMeasure daxilində obyektlərin yaradılması — klassik performans səhvdir. onMeasure dəfələrlə çağrıla bildiyindən (hər layout tələbində), bu metod daxilində obyektlərin (Paint, Rect, String) yaradılması yaddaşı zibilləyir və zibil toplayıcısını təhrik edir. Bütün obyektlər View-in konstruktorunda bir dəfə yaradılmalı, onMeasure-də isə yalnız ölçülərin hesablanması məntiqi yerinə yetirilməlidir. Eyni qayda onDraw və onLayout üçün də keçərlidir.
measureChildWithMargins — ViewGroup-un qorunan metodudur, bir uşaq View-i onun MarginLayoutParams nəzərə alınmaqla ölçür. Metod valideynin MeasureSpec-ini və en və hündürlük üzrə yığılmış yerdəyişmələri qəbul edir. Avtomatik olaraq uşaq View üçün MeasureSpec-i düzəldir, valideynin padding-lərini və uşağın margin-lərini çıxarır və düzəldilmiş MeasureSpec-i child.measure()-a ötürür.
Qabaqcıl ölçmə məntiqi üçün ViewGroup measureChild(View child, int parentWidthSpec, int parentHeightSpec)-i yenidən təyin edə və ya hər bir uşaq elementi üçün birbaşa MeasureSpec ilə işləyə bilər. Məsələn, LinearLayout onMeasure-də bütün uşaq View-lər arasında dövr edir, hər birini onun layout_weight nəzərə alınmaqla ölçür və qalan məkanı proporsional olaraq bölüşdürür. Bu yanaşma ixtiyari düzülüş alqoritmlərini həyata keçirməyə imkan verir.
Ölçmə nəticələrinin ölçü keşi (measure cache) mexanizmi vasitəsilə keşləşdirilməsi müəyyən ViewGroup-larda setMeasureWithLargestChildEnabled bayrağı vasitəsilə mövcuddur. Lakin əksər hallarda onMeasure hər hansı layout dəyişikliyində təkrar çağırılır və keşləşdirmə tətbiq edilmir. Xüsusi ViewGroup-larda keşə güvənmək əvəzinə onMeasure-də hesablamaları minimuma endirmək tövsiyə olunur.
Tez-tez verilən suallar
Bəli, əgər xüsusi View birbaşa View sinfindən miras alırsa. Əgər miras TextView, ImageView və ya Button-dan onların standart ölçüləri ilə gedirsə, onMeasure-i yenidən təyin etmək lazım deyil. ViewGroup üçün onMeasure-in yenidən təyin edilməsi həmişə tələb olunur — əks halda uşaq elementləri düzgün ölçülməyəcək.
Android sistemi IllegalStateException istisnası atır, mətni: “The View did not call setMeasuredDimension”. Bu istisna onMeasure başa çatdıqdan sonra measure() metodunda, yekun ölçülər sıfır qaldıqda yaranır. İstisna try-catch ilə işlənməzsə, tətbiq çökür.
getMeasuredWidth() onMeasure-də təyin edilmiş ölçünü qaytarır (ölçmə mərhələsi). getWidth() View-in onLayout-da bütün mövqe düzəlişləri nəzərə alındıqdan sonra aldığı faktiki ölçünü qaytarır. Əksər View-lər üçün bu dəyərlər üst-üstə düşür, lakin xüsusi ViewGroup-larda fərqlənə bilər.
Xeyr, onMeasure yalnız ölçülərin hesablanması üçün nəzərdə tutulub. Bu metoddə vəziyyətin dəyişdirilməsi, animasiyaların işə salınması, şəbəkə ilə işləmə və ya məlumatların yenilənməsi Android arxitekturasını pozur və rekursiv measure çağırışlarına səbəb ola bilər, çünki vəziyyətin dəyişməsi requestLayout-u tetikleyə bilər.
ConstraintLayout verilmiş məhdudiyyətlər əsasında uşaq elementlərinin ölçülməsini müstəqil idarə edir. Əgər ConstraintLayout daxilində xüsusi View onMeasure-i yenidən təyin edirsə, ConstraintLayout-dan ötürülən MeasureSpec-i düzgün emal etməlidir, əks halda məhdudiyyətlər işləməyə bilər. ConstraintLayout hesablama üçün öz WidgetContainer ilə iki prokedli alqoritmdən istifadə edir.
Xülasə
Açar təslim mobil tətbiq hazırlayacağıq
IT Sectr 2017-ci ildən startaplar və bizneslər üçün iOS və Android tətbiqləri yaradır. Sizə məsləhət verəcəyik və ən yaxşı həlli təklif edəcəyik.
Həm də oxuyun