onLayout() — bu ota-ona konteynerning koordinata tekisligidagi bola View-larning pozitsiyalari va o'lchamlarini aniqlaydigan ViewGroup sinfi metodidir. Android tizimi onLayout-ni o'lchash fazasidan (onMeasure) so'ng, har bir bola View uchun o'lchangan kenglik va balandlik ma'lum bo'lganda chaqiradi. Android Developers Documentation (2026) ma'lumotlariga köra, onLayout har qanday maxsus ViewGroup-da qayta yozilishi majburiy bo'lgan metoddir, chunki ViewGroup-ning standart amalga oshirilishi bolalarni avtomatik joylashtirishni bajarmaydi.
Asosiy ma'lumotlar
onLayout(boolean changed, int l, int t, int r, int b) — bu ota-ona konteyner ichidagi bola View-larni joylashtirish uchun tizim tomonidan chaqiriladigan ViewGroup sinfining protected metodidir. Dasturchi ushbu metodni elementlarning nostandart joylashuvi (kaskad, tör, shaxmat tartibi yoki ixtiyoriy koordinatalar) bilan maxsus ViewGroup yaratganda qayta yozadi. Har bir bola View child.layout() chaqiriḡi orqali o'zining yakuniy chegaralarini oladi.
changed parametri ViewGroup-ning o'z pozitsiyasi yoki o'lchami oxirgi layout-dan beri o'zgarganligini ko'rsatadi. Agar changed true bo'lsa, barcha bola elementlar ham qayta joylashtirishga muhtoj bo'lishi mumkin. l, t, r, b parametrlari ViewGroup-ning ota-onasining koordinata tizimidagi yuqori chap va pastki o'ng burchaklarining koordinatalaridir. onLayout ichida dasturchi ushbu qiymatlardan bolalarni joylashtirish uchun boshlang'ich koordinatalar sifatida foydalanadi.
ViewGroup — onLayout-ni qayta yozadigan yagona sinfdir. Oddiy View (ViewGroup emas) bola elementlarga ega emas va onLayout-ga muhtoj emas — uning o'z joylashuvi ota-ona konteyner tomonidan boshqariladi. Oddiy View onLayout-ni qayta yozsa ham, tizim uni chaqirmaydi. Bu onLayout-ni har qanday View uchun chaqiriladigan onMeasure-dan asosiy farqidir.
Layout fazasi ildiz View-da layout(int l, int t, int r, int b) ochiq metodining chaqirilishi bilan boshlanadi. Bu metod View-ning o'z yakuniy koordinatalarini belgilaydi va agar View ViewGroup bo'lsa, onLayout-ni chaqiradi. Keyin onLayout har bir bola element uchun rekursiv ravishda child.layout() chaqiradi va jarayon ierarxiya bo'ylab pastga qarab davom etadi. Shunday qilib, layout ildizdan barglarga tarqaladi.
onLayout chaqirilishidan oldin tizim View o'lchamlari avvalgi tsiklga nisbatan o'zgarganligini tekshiradi. Agar o'lchamlar o'zgarmagan bo'lsa va requestLayout chaqirilmagan bo'lsa, onLayout chaqirilmasligi mumkin — tizim avvalgi layout natijalaridan foydalanadi. Bu faqat mazmun o'zgaradigan, lekin o'lchamlar o'zgarmaydigan animatsiyalar yoki skrollash vaqtida keraksiz pozitsiya qayta hisoblashlarining oldini oluvchi optimallashtirishdir.
requestLayout() — View layout-ining eskirganligi va qayta hisoblash talab qilinishi haqida tizimga xabar beruvchi View metodidir. requestLayout chaqiruvi to'liq tsiklga olib keladi: avval onMeasure, keyin onLayout, so'ngra onDraw chaqiriladi. Faqat qayta chizishni boshlaydigan invalidate-dan farqli o'laroq, requestLayout o'lchamlar va pozitsiyalarning to'liq qayta hisoblanishini boshlaydi. Haddan tashqari chaqirish requestLayout ishlash muammolarining keng tarqalgan sababidir.
l (left) — ViewGroup-ning ota-ona koordinata tizimidagi chap chekkasining X koordinatasi. t (top) — yuqori chekkasining Y koordinatasi. r (right) — o'ng chekkasining X koordinatasi. b (bottom) — pastki chekkasining Y koordinatasi. ViewGroup-ning kengligi r - l, balandligi esa b - t sifatida hisoblanadi. Bu koordinatalar ViewGroup-ning o'z padding-larini o'z ichiga oladi.
onLayout ichida dasturchi har bir bola View uchun child.layout(int childLeft, int childTop, int childRight, int childBottom) chaqiradi. child.layout-ga uzatiladigan koordinatalar ota-ona ViewGroup-ning koordinata tizimida bo'lishi kerak. Odatda, childLeft va childTop ota-onaning padding-lari hisobga olingan holda hisoblanadi: childLeft = l + paddingLeft + offsetX, childTop = t + paddingTop + offsetY.
| Parametr | Tavsif | Odatiy foydalanish |
|---|---|---|
| l (left) | ViewGroup-ning ota-onadagi chap chekka koordinatasi | Bola elementlar uchun X o'qidagi boshlang'ich nuqta |
| t (top) | ViewGroup-ning ota-onadagi yuqori chekka koordinatasi | Bola elementlar uchun Y o'qidagi boshlang'ich nuqta |
| r (right) | ViewGroup-ning ota-onadagi o'ng chekka koordinatasi | Kenglikning yuqori chegarasi, r - l = getWidth() |
| b (bottom) | ViewGroup-ning ota-onadagi pastki chekka koordinatasi | Balandlikning yuqori chegarasi, b - t = getHeight() |
Bola koordinatalari quyidagi formula bo'yicha hisoblanadi: childLeft = l + paddingLeft + (marginLeft if present), childRight = childLeft + child.getMeasuredWidth(). Vertikal uchun ham xuddi shunday: childTop = t + paddingTop + (marginTop), childBottom = childTop + child.getMeasuredHeight(). Ushbu to'rt qiymat hisoblangandan so'ng, child.layout(childLeft, childTop, childRight, childBottom) chaqiriladi.
FlowLayout yaratamiz — bola View-larni qatorlarga joylashtiradigan, joriy qator to'lganda elementlarni yangi qatorga ōtkazadigan maxsus ViewGroup. Bu bir tekislikda wrap bilan Flexbox-ning analogidir. onLayout barcha bola View-larni aylanib chiqadi, har biri uchun pozitsiyani hisoblaydi va child.layout() chaqiradi.
class FlowLayout(context: Context)
: ViewGroup(context) {
private val horizontalSpacing = 12
private val verticalSpacing = 8
override fun onMeasure(widthMeasureSpec: Int,
heightMeasureSpec: Int) {
val parentWidth =
MeasureSpec.getSize(widthMeasureSpec)
var rowX = paddingLeft
var rowY = paddingTop
var maxRowHeight = 0
for (i in 0 until childCount) {
val child = getChildAt(i)
measureChildWithMargins(child,
widthMeasureSpec, 0,
heightMeasureSpec, 0)
if (rowX + child.measuredWidth >
parentWidth - paddingRight) {
rowX = paddingLeft
rowY += maxRowHeight + verticalSpacing
maxRowHeight = 0
}
rowX += child.measuredWidth +
horizontalSpacing
maxRowHeight = maxOf(maxRowHeight,
child.measuredHeight)
}
val totalHeight = rowY + maxRowHeight +
paddingBottom
setMeasuredDimension(
resolveSize(parentWidth, widthMeasureSpec),
resolveSize(totalHeight, heightMeasureSpec))
}
override fun onLayout(changed: Boolean,
l: Int, t: Int,
r: Int, b: Int) {
val parentWidth = r - l
var rowX = paddingLeft
var rowY = paddingTop
var maxRowHeight = 0
for (i in 0 until childCount) {
val child = getChildAt(i)
val cw = child.measuredWidth
val ch = child.measuredHeight
if (rowX + cw >
parentWidth - paddingRight) {
rowX = paddingLeft
rowY += maxRowHeight + verticalSpacing
maxRowHeight = 0
}
child.layout(rowX, rowY,
rowX + cw, rowY + ch)
rowX += cw + horizontalSpacing
maxRowHeight =
maxOf(maxRowHeight, ch)
}
}
override fun generateLayoutParams(attrs: AttributeSet?)
: LayoutParams =
MarginLayoutParams(context, attrs)
}
onMeasure va onLayout — View hayot siklidagi ikki ketma-ket faza bo'lib, tubdan farqli vazifalarni bajaradi. onMeasure View-ning istalgan (measured) o'lchamlarini aniqlaydi, onLayout esa haqiqiy (actual) koordinatalar va o'lchamlarni belgilaydi. Asosiy farq: onMeasure da o'lchamlar vaqtinchalik bo'lishi va keyinroq ota-ona tomonidan tuzatilishi mumkin, onLayout da esa har bir bola View-ning yakuniy pozitsiyasi belgilanadi.
onMeasure har bir View, jumladan barg (TextView, ImageView, Button) uchun chaqiriladi. onLayout faqat ViewGroup uchun chaqiriladi. Buning sababi, joylashtirish ota-ona konteynerning mas'uliyati, View-ning o'zi emas. Barg View o'z pozitsiyasini ota-onaning onLayout-dan chaqiriladigan layout() orqali oladi.
getMeasuredWidth() va getMeasuredHeight() onMeasure-dan keyin, getWidth() va getHeight() esa faqat onLayout-dan keyin mavjud bo'ladi. Agar onMeasure ichida getWidth() ga murojaat qilsangiz, oldingi tsiklning qiymati yoki nol qaytariladi. Shuning uchun onMeasure da o'lchamlarni hisoblash uchun MeasureSpec va ketma-ket children dan foydalanish kerak.
Padding hisobga olinmasdan joylashtirish — onLayout amalga oshirishdagi birinchi xato. Dasturchi ko'pincha ota-onaning paddingLeft va paddingTop qiymatlarini bola View-larning boshlang'ich koordinatalariga qo'shishni unutadi. Natijada children ViewGroup-ning chekkasida ko'rsatiladi, setPadding() yoki XML da belgilangan oralıqlarni hisobga olmaydi. To'g'ri hisoblash: childLeft = paddingLeft + offsetX.
Ko'rinmaydigan bolalar uchun layout chaqirish — ikkinchi keng tarqalgan muammo. Agar ViewGroup tarkibida visibility qiymati GONE bo'lgan bola View-lar bo'lsa, ularni joylashtirish shart emas — ular joy egallamaydi. Biroq onLayout bu holatni to'g'ri boshqarishi, GONE bolalarni o'tkazib yuborishi kerak. INVISIBLE bolalar uchun layout hali ham chaqirilishi kerak — ular ko'rsatilmasa ham, o'z joyini saqlab qoladi.
changed parametrini e'tiborsiz qoldirish — uchinchi xato. changed parametri ViewGroup-ning o'lchamlari yoki pozitsiyasi o'zgarganligini ko'rsatadi. Agar changed == false bo'lsa, keshlangan koordinatalardan foydalanish va barcha bola elementlarning layout-ni qayta hisoblamaslik mumkin. Biroq layout-ni to'liq keshlash qiyin vazifa va ko'pchilik onLayout amalga oshirishlari har safar barcha elementlarni qayta hisoblaydi. Bu bolalar soni kam bo'lganda maqbuldir.
Ko'p beriladigan savollar
Mumkin, agar ViewGroup standart LayoutParams dan foydalansa va maxsus joylashtirish mantig'ini qo'shmasa. Biroq ViewGroup-dagi onLayout-ning standart amalga oshirilishi hech qanday harakat bajarmaydi — bola elementlar joylashtirilmaydi. Amalda barcha ViewGroup-lar (LinearLayout, RelativeLayout, FrameLayout) onLayout-ni qayta yozadi.
layout() — tizim yoki ota-ona ViewGroup tomonidan chaqiriladigan View-ning ochiq final metodidir. U View-ning o'z koordinatalarini belgilaydi va agar View ViewGroup bo'lsa, onLayout-ni chaqiradi. onLayout() — dasturchi tomonidan bola elementlarning maxsus joylashtirilishi uchun qayta yoziladigan protected metoddir.
Texnik jihatdan — ha, chaqira oladi. Lekin bu qat'iyan tavsiya etilmaydi, chunki cheksiz rekursiyaga olib keladi: requestLayout → onMeasure → onLayout → requestLayout. Agar onLayout ichida requestLayout chaqirilsa, tizim StackOverflowError istisnosini tashlaydi. Barcha o'lcham o'zgarishlari onLayout-dan oldin bajarilishi kerak.
Layout animatsiyalari (LayoutTransition) bola View-larning pozitsiya o'zgarishlarini tutib oladi va o'tish animatsiyasini qo'llaydi. LayoutTransition yoqilganida, onLayout avval yakuniy pozitsiyalarni belgilaydi, keyin LayoutTransition eski pozitsiyadan yangisiga harakatni animatsiya qiladi. Bu to'g'ri yakuniy koordinatalar bilan onLayout-ni to'g'ri amalga oshirishni talab qiladi.
invalidate() faqat draw fazasini (qayta chizish) ishga tushiradi, measure va layout ga ta'sir qilmaydi. onLayout-ni ishga tushirish uchun requestLayout() chaqirilishi kerak, u to'liq tsiklni boshlaydi: measure → layout → draw. invalidate o'lchamlar va pozitsiyalar o'zgarmaganda tashqi ko'rinishni yangilash uchun samaraliroqdir.
Xulosa
Biz kalit topshirig'i bilan mobil ilovani ishlab chiqamiz
IT Sectr 2017-yildan beri startaplar va korxonalar uchun iOS va Android ilovalarini yaratadi. Biz sizga maslahat beramiz va eng yaxshi yechimni taklif qilamiz.