View Lifecycle — bu nima, onMeasure onLayout onDraw jarayonlari

Muallif: IT Sectr Nashr etilgan: 2026-03-05 O'qish vaqti: 10 daq

View Lifecycle — Android foydalanuvchi interfeysi elementi (View) ni ekranda ko'rsatish va qayta ko'rsatish uchun chaqiradigan metodlar ketma-ketligidir. Activity yoki Fragmentdan farqli o'laroq, View kengaytirilgan hayot aylanishiga ega bo'lmagan, ammo qat'iy uch fazali jarayondan o'tadigan yengil komponentdir: onMeasure (o'lchash), onLayout (joylashtirish), onDraw (chizish). View Lifecycle-ni tushunish maxsus View-lar yaratish, ishlashni optimallashtirish va ko'rsatish muammolarini hal qilish uchun zarurdir. Google ma'lumotlariga ko'ra, to'g'ri amalga oshirilganda maxsus View-lar UI-ni 15–40% tezlashtiradi standart ichma-ich ViewGroup kombinatsiyasi bilan solishtirganda. Android-ning maxsus View-lar haqidagi hujjatlari onMeasure, onLayout va onDraw-ni View Lifecycle-ning uch ustuni sifatida tasvirlaydi.

Asosiy ma'lumotlar

  • View Lifecycle uch fazadan iborat: onMeasure (o'lchamlar), onLayout (pozitsiyalar), onDraw (chizish) — va invalidate() yoki requestLayout() chaqirilganda ishga tushadi.
  • onMeasure View-ning kengligi va balandligini MeasureSpec (AT_MOST, EXACTLY, UNSPECIFIED) asosida hisoblaydi.
  • onLayout ViewGroup ichidagi bola View-larni joylashtiradi, ularning left, top, right, bottom koordinatalarini aniqlaydi.
  • onDraw View mazmunini Canvas-da chizadi: fon, matn, shakllar, tasvirlar.
  • Noto'g'ri View Lifecycle — UI ishlash muammolarining (jank, dropped frames) va iyerarxiyaning asosiy sababi.

View Lifecycle — Android-da bu nima

View Lifecycle — Android View (va ViewGroup) o'zini ekranda ko'rsatish uchun o'tadigan jarayondir. Activity yoki Fragmentdan farqli o'laroq, View da onStart/onStop/onDestroy mavjud emas — uning „hayoti” o'lchash, joylashtirish va chizishning davriy jarayonidan iborat. Bu tsikl View har safar ko'rsatilishi yoki qayta chizilishi kerak bo'lganda ishga tushadi.

View Lifecycle-ning uch fazasi:

  • onMeasure(int widthMeasureSpec, int heightMeasureSpec) — View-ning istalgan o'lchamlarini aniqlaydi. Tizim MeasureSpec — ruxsat etilgan o'lchamlar haqida ko'rsatma beradi (aniq qiymat, maksimal yoki cheklovsiz).
  • onLayout(boolean changed, int left, int top, int right, int bottom) — View va uning avlodlarini ekranga joylashtiradi. View uchun o'z chegaralarini, ViewGroup uchun bola elementlarning pozitsiyalarini belgilaydi.
  • onDraw(Canvas canvas) — View mazmunini berilgan Canvas-da chizadi. Tizim buyruqlarni bitmap yoki GPU ga tarjima qiladigan Canvas-ni taqdim etadi.

View Lifecycle-ning to'liq tsikli, shuningdek, View-ning oynaga biriktirilishi bilan bog'liq metodlarni o'z ichiga oladi: onAttachedToWindow (View oynaga biriktirilgan, HW acceleration mavjud) va onDetachedFromWindow (View ajratilgan, resurslar bo'shatiladi). Bu metodlar View hayoti davomida bir marta chaqiriladi va animatsiyalar, sensorlarni ro'yxatdan o'tkazish/bekor qilish uchun muhimdir.

Android Performance Blog ma'lumotlariga ko'ra, UI ishlash muammolarining (jank, kadr tushirish) 65% onMeasure va onDraw-ning noto'g'ri amalga oshirilishi bilan bog'liq: haddan tashqari override, keraksiz requestLayout() chaqirish, onDraw-da obyekt yaratish.

onMeasure: View o'lchamlarini o'lchash

onMeasure — View Lifecycle-ning eng muhim va eng murakkab fazasi. Bu bosqichda Android View ekranda qancha joy egallashini aniqlaydi. Tizim rejim va o'lchamdan iborat int-ga yig'ilgan ko'rsatmalar — MeasureSpec-ni uzatadi.

MeasureSpec-ning uch rejimi:

RejimDoimiyMa'nosiMisol
EXACTLYMeasureSpec.EXACTLYOta tomonidan belgilangan aniq o'lcham (match_parent yoki qat'iy kenglik)width=400dp → MeasureSpec(400, EXACTLY)
AT_MOSTMeasureSpec.AT_MOSTView belgilangan maksimalgacha o'lchamda bo'lishi mumkin (wrap_content)width ≤ 400dp → MeasureSpec(400, AT_MOST)
UNSPECIFIEDMeasureSpec.UNSPECIFIEDCheklovsiz — View istalgan o'lchamda bo'lishi mumkin (ScrollView, RecyclerView)kenglik cheklanmagan → MeasureSpec(0, UNSPECIFIED)

onMeasure amalga oshirish:

  • O'lchangan o'lchamlarni saqlash uchun setMeasuredDimension(int width, int height) chaqiriladi.
  • Padding hisobga olinadi — mavjud kenglikdan getPaddingLeft() + getPaddingRight() ayiriladi.
  • ViewGroup uchun — barcha avlodlar measureChild() yoki measureChildWithMargins() orqali o'lchanadi.
  • wrap_content uchun — mazmun (matn, tasvir) bo'yicha o'lcham hisoblanadi.
  • onMeasure ichida requestLayout() chaqirilmaydi — bu cheksiz tsiklga olib keladi.

Oddiy xato: wrap_content da MeasureSpec-ni hisobga olmaslik. View wrap_content ga o'rnatilgan bo'lsa, lekin onMeasure AT_MOST-ni qayta ishlamasa va qat'iy o'lcham qaytarsa, View yo kesiladi yoki kerak bo'lganidan ko'proq joy egallaydi.

onLayout: View-ni ekranga joylashtirish

onLayout — View yoki ViewGroup o'z avlodlarini o'z chegaralari ichida joylashtiradigan faza. Oddiy View (ViewGroup emas) uchun onLayout talab qilinmaydi — tizim ota tomondan uzatilgan parametrlar bilan layout() ni chaqiradi. ViewGroup uchun onLayout majburiydir — usiz bola View-lar joylashtirilmaydi.

onLayout imzosi:

java
@Override
protected void onLayout(boolean changed,
        int left, int top,
        int right, int bottom) {
    // bola View-larni joylashtirish
}

changed parametri View-ning pozitsiyasi yoki o'lchami oldingi layout bilan solishtirganda o'zgarganligini ko'rsatadi. Agar false bo'lsa — View optimallashtirish uchun avlodlar pozitsiyalarini qayta hisoblashni o'tkazib yuborishi mumkin.

ViewGroup uchun onLayout:

  • Barcha avlodlarni getChildCount() va getChildAt(i) orqali ko'rib chiqadi.
  • Har bir avlod uchun left, top, right, bottom — ViewGroup ichidagi koordinatalar (padding bilan) aniqlanadi.
  • Har bir avlod uchun child.layout(l, t, r, b) chaqiriladi.
  • Gravity, margins, alignment hisobga olinadi.

onLayout onMeasure dan keyin chaqiriladi — o'lchangan o'lchamlar getMeasuredWidth()/getMeasuredHeight() orqali mavjud. Agar bola View layout() dan keyin boshqa haqiqiy o'lchamlarga ega bo'lsa, qayta o'lchash uchun requestLayout() chaqiriladi. Bu „layout pass” deb ataladi va qayta hisoblashlarning zanjirli reaktsiyasiga olib kelishi mumkin.

onDraw: Canvas mazmunini chizish

onDraw — View o'zini Canvas-da chizadigan faza. Bu onMeasure va onLayoutsiz bir necha marta chaqirilishi mumkin bo'lgan yagona fazadir — agar View invalidate() deb belgilangan bo'lsa. Canvas chizish uchun API taqdim etadi: drawLine, drawRect, drawCircle, drawText, drawBitmap va drawPath.

onDraw qoidalari:

  • onDraw da obyekt yaratmang — har bir onDraw chaqiruvi oldindan yaratilgan obyektlardan (Path, Paint, Rect) foydalanishi kerak. onDraw da obyekt yaratish GC pauzalariga va kadr tushishiga olib keladi.
  • onDraw ichida requestLayout() yoki invalidate() chaqirmang — bu cheksiz qayta chizish tsiklini ishga tushiradi.
  • Uzoq hisob-kitoblarni bajarmang — onDraw UI thread-da bajariladi. Murakkab hisoblashlar background thread-ga chiqarilishi yoki oldindan hisoblanishi kerak.
  • Hardware Acceleration dan foydalaning — API 14+ dan Canvas GPU orqali ishlashi mumkin. Murakkab grafika (gradientlar, soyalar, aylantirishlar) uchun HW tezlashtirish ishlashni 300% gacha oshiradi.
  • Faqat ko'rinadigan maydonni chizing — ko'rinmaydigan qismlarni kesish uchun canvas.clipRect() dan foydalaning.

ViewGroup da chizish tartibi: fon (setBackgroundDrawable) → onDraw (mazmun) → dispatchDraw (bola View-lar) → onDrawForeground (old fon). dispatchDraw har bir avlodning onDraw-ni chaqiradi. dispatchDraw-ni override qilish bola elementlar ustiga effektlar qo'llash uchun ishlatiladi.

Android Vitals statistikasiga ko'ra, onDraw da kadr tushishining eng keng tarqalgan sabablari — metod ichida obyekt yaratish (48%), decodeResource chaqiruvi (22%) va keshsiz Path bilan murakkab operatsiyalar (15%).

Invalidation: View qachon qayta chiziladi

Invalidation — View-ni qayta chizishni ishga tushiradigan mexanizm. invalidate() chaqiruvi View-ni „iflos” deb belgilaydi va keyingi chizish siklida onDraw chaqiruvini rejalashtiradi. requestLayout() chaqiruvi — og'irroq operatsiya bo'lib, to'liq tsiklni ishga tushiradi: onMeasure → onLayout → onDraw.

MetodNima qiladiQachon ishlatish kerak
invalidate()onMeasure/onLayoutsiz onDraw chaqiradiFaqat tashqi ko'rinish o'zgargan (rang, matn, jarayon)
invalidate(Rect)Faqat ko'rsatilgan maydonni qayta chizadiView qismi o'zgargan — animatsiya, tanlash
postInvalidate()UI bo'lmagan thread dan invalidate chaqiradiFon thread-i chizish uchun ma'lumotlarni yangilagan
requestLayout()onMeasure → onLayout → onDraw ishga tushiradiMazmun o'lchami o'zgargan (matn, tasvir)
forceLayout()View-ni majburiy qayta o'lchash uchun belgilaydiIchki holat o'zgargan, o'lcham o'zgargan bo'lishi mumkin

Animatsiyalar va View Lifecycle: ViewPropertyAnimator va ValueAnimator animatsiyaning har bir kadrida invalidate() chaqiradi. ObjectAnimator View da setter chaqiradi, agar setter o'lchamni (width/height) o'zgartirsa, avtomatik ravishda requestLayout() chaqiradi. Bu murakkab ViewGroup lar uchun qimmatga tushishi mumkin: har bir requestLayout ildiz view-gacha to'liq iyerarxiyani ishga tushiradi.

Optimallashtirish qoidasi: requestLayout() o'rniga invalidate() faqat tashqi ko'rinish o'zgarganda (rang, shaffoflik, o'lchamsiz aylantirish). requestLayout faqat o'lchamlar yoki o'lchamga ta'sir qiluvchi mazmun o'zgarganda ishlating.

Maxsus View optimallashtirish: eng yaxshi amaliyotlar

Maxsus View-lar — noyob UI yaratish uchun kuchli vosita, ammo ular ishlash qoidalariga qat'iy rioya qilishni talab qiladi. View Lifecycle optimallashtirish bo'yicha Google-ning asosiy tavsiyalari.

  • Hisoblash mumkin bo'lgan hamma narsani oldindan hisoblang — o'lchamlar, koordinatalar, Path yo'li, gradient ranglari. onDraw da faqat chizishdan foydalaning.
  • O'lchash natijasini keshlang — agar View qat'iy o'lchamlarga ega bo'lsa, MeasureSpec-ni saqlang va qo'shimcha hisoblashlarsiz setMeasuredDimension qaytaring.
  • ViewConfiguration dan foydalaning — getScaledTouchSlop, getScaledMinimumFlingVelocity — teginishlarni boshqarish uchun.
  • Iyerarxiyadagi View sonini minimallashtiring — bir nechta elementlarni birlashtirgan maxsus View-lar har doim 3–5 ichma-ich View dan iborat ViewGroup dan tezroq. Google bir ekranda 10 dan ortiq ichma-ich View tavsiya etmaydi.
  • Yassi iyerarxiya uchun ConstraintLayout dan foydalaning — bitta ViewGroup quradi, ishlashi RelativeLayout ga yaqin, ammo ichma-ichliksiz.
  • Qayta chizishda hardware layer-ni o'chiring — animatsiyali View-lar uchun setLayerType(LAYER_TYPE_HARDWARE) va tugagandan so'ng setLayerType(LAYER_TYPE_NONE) ishlating.
  • Rect bilan invalidate() ishlating — butun View-ni emas, faqat o'zgargan maydonni qayta chizing.
  • Overdraw dan saqlaning — keraksiz chizishlarni aniqlash uchun Android Studio da Profile GPU Rendering dan foydalaning. Google ilovalari uchun o'rtacha overdraw — 1.5x, maksimal tavsiya etilgan — 2.5x.

Kotlin-da View kod misollari

Misol 1: Maxsus View — jarayon ko'rsatkichi

onMeasure, onDraw va invalidate ning to'g'ri amalga oshirilishi bilan oddiy dumaloq jarayon ko'rsatkichi.

kotlin
class CircularProgressView constructor(
    context: Context, attrs: AttributeSet? = null
) : View(context, attrs) {

    private val progressPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
        color = Color.BLUE
        style = Paint.Style.STROKE
        strokeWidth = 8f
        strokeCap = Paint.Cap.ROUND
    }

    private val backgroundPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
        color = Color.LTGRAY
        style = Paint.Style.STROKE
        strokeWidth = 8f
    }

    private var progress = 0f
    private var viewWidth = 0
    private var viewHeight = 0

    fun setProgress(value: Float) {
        progress = value.coerceIn(0f, 100f)
        invalidate()
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        val desiredSize = 100 * resources.displayMetrics.density.toInt()
        val width = MeasureSpec.getSize(widthMeasureSpec)
        val height = MeasureSpec.getSize(heightMeasureSpec)
        val size = minOf(width, height).coerceAtLeast(desiredSize)
        setMeasuredDimension(size, size)
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        val padding = progressPaint.strokeWidth / 2
        val radius = (minOf(viewWidth, viewHeight) - padding) / 2
        val cx = viewWidth / 2f
        val cy = viewHeight / 2f
        canvas.drawCircle(cx, cy, radius, backgroundPaint)
        val sweepAngle = (progress / 100f) * 360f
        canvas.drawArc(cx - radius, cy - radius, cx + radius, cy + radius,
            -90f, sweepAngle, false, progressPaint)
    }

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
        viewWidth = w
        viewHeight = h
    }
}

Dumaloq jarayon chizig'i: onMeasure MeasureSpec asosida kvadrat o'lcham qaytaradi, onSizeChanged o'lchamlarni eslab qoladi, onDraw fon va jarayon yoyini chizadi. Invalidate jarayon o'zgarganda chaqiriladi — onMeasure/onLayout ta'sirlanmaydi. Paint konstruktorda bir marta yaratiladi, onDraw da emas.

Misol 2: ViewGroup — oddiy FlowLayout

Bola View-larni qatorlarga joylashtiradigan maxsus ViewGroup (Flexbox wrap kabi).

kotlin
class FlowLayout constructor(
    context: Context, attrs: AttributeSet? = null
) : ViewGroup(context, attrs) {

    private val horizontalSpacing = 8.dpToPx(resources)
    private val verticalSpacing = 8.dpToPx(resources)

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        val width = MeasureSpec.getSize(widthMeasureSpec)
        var totalHeight = paddingTop + paddingBottom
        var rowWidth = paddingLeft
        var rowHeight = 0
        for (i in 0 until childCount) {
            val child = getChildAt(i)
            measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, totalHeight)
            if (rowWidth + child.measuredWidth > width - paddingRight) {
                totalHeight += rowHeight + verticalSpacing
                rowWidth = paddingLeft
                rowHeight = 0
            }
            rowWidth += child.measuredWidth + horizontalSpacing
            rowHeight = maxOf(rowHeight, child.measuredHeight)
        }
        totalHeight += rowHeight
        setMeasuredDimension(
            MeasureSpec.getSize(widthMeasureSpec),
            resolveSize(totalHeight, heightMeasureSpec)
        )
    }

    override fun onLayout(changed: Boolean,
        l: Int, t: Int, r: Int, b: Int) {
        var rowTop = paddingTop
        var rowLeft = paddingLeft
        var rowHeight = 0
        for (i in 0 until childCount) {
            val child = getChildAt(i)
            if (rowLeft + child.measuredWidth > r - paddingRight) {
                rowTop += rowHeight + verticalSpacing
                rowLeft = paddingLeft
                rowHeight = 0
            }
            child.layout(rowLeft, rowTop, rowLeft + child.measuredWidth, rowTop + child.measuredHeight)
            rowLeft += child.measuredWidth + horizontalSpacing
            rowHeight = maxOf(rowHeight, child.measuredHeight)
        }
    }

    override fun generateLayoutParams(attrs: AttributeSet?): LayoutParams {
        return MarginLayoutParams(context, attrs)
    }
}

FlowLayout onMeasure-ni override qiladi: har bir avlodni o'lchaydi, kenglik oshib ketganda yangi qatorga o'tkazadi, umumiy balandlikni hisoblaydi. onLayout bolalarni qator o'tishlari bilan koordinatalar bo'yicha joylashtiradi. generateLayoutParams bola View-larda margin qo'llab-quvvatlash uchun MarginLayoutParams qaytaradi.

Misol 3: Path keshlash bilan onDraw

Maxsus View Path-ni oldindan hisoblab va keshlab, silliq Bezier egri chizig'ini chizadi.

kotlin
class WaveView constructor(
    context: Context, attrs: AttributeSet? = null
) : View(context, attrs) {

    private val wavePaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
        color = Color.parseColor("#4A90D9")
        style = Paint.Style.FILL
    }

    private val wavePath = Path()
    private var isPathDirty = true
    private var viewWidth = 0
    private var viewHeight = 0

    fun refreshWave() {
        isPathDirty = true
        invalidate()
    }

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
        viewWidth = w
        viewHeight = h
        isPathDirty = true
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        if (isPathDirty) {
            wavePath.reset()
            val amplitude = viewHeight * 0.1f
            wavePath.moveTo(0f, viewHeight * 0.5f)
            for (x in 0..viewWidth step 4) {
                val y = viewHeight * 0.5f + amplitude * Math.sin(x * 2 * Math.PI / viewWidth).toFloat()
                wavePath.lineTo(x.toFloat(), y)
            }
            wavePath.lineTo(viewWidth.toFloat(), viewHeight.toFloat())
            wavePath.lineTo(0f, viewHeight.toFloat())
            wavePath.close()
            isPathDirty = false
        }
        canvas.drawPath(wavePath, wavePaint)
    }
}

Path keshlash: isPathDirty = true faqat View o'lchamlari o'zgarganda yoki refreshWave() chaqirilganda. onDraw da Path faqat „iflos” bo'lsa qayta hisoblanadi. Bu har bir animatsiya kadrida Bezier egri chizig'ini qayta hisoblashning oldini oladi, CPU-ni tejaydi.

Tez-tez so'raladigan savollar

View Lifecycle Activity Lifecycle dan qanday farq qiladi?

View Lifecycle — Activity yaratilishi/yo'q qilinishiga bog'liq bo'lmagan davriy chizish jarayoni (onMeasure → onLayout → onDraw). View da onStart/onStop yo'q — u yo ko'rinadi (oynaga biriktirilgan) yoki yo'q. Activity Lifecycle ilova komponentining holatini boshqaradi, View Lifecycle — UI chizilishini.

requestLayout() ishlashga qanday ta'sir qiladi?

requestLayout() ildizdan butun View daraxti uchun to'liq tsiklni ishga tushiradi: onMeasure → onLayout → onDraw. Agar requestLayout() tez-tez chaqirilsa (masalan, animatsiyaning har bir kadrida), bu jank va kadr tushishiga olib keladi. Google ma'lumotlariga ko'ra, bitta requestLayout 10 elementli ViewGroup da o'rtacha 2–5 ms davom etadi. Animatsiyalar uchun invalidate() dan foydalaning.

onAttachedToWindow qachon chaqiriladi?

onAttachedToWindow View oynaga (Window) biriktirilganda — ko'rinadigan iyerarxiyaning bir qismiga aylanganda chaqiriladi. Shu payt View HW acceleration va Window resurslariga (WindowManager, Display) kirish huquqini oladi. onAttachedToWindow animatsiya tinglovchilarini va View ko'rinadigan vaqt davomida yashovchi BroadcastReceiver-ni ro'yxatdan o'tkazish uchun to'g'ri joydir.

Overdraw nima va uni qanday kamaytirish mumkin?

Overdraw — piksel bir kadrda bir necha marta chiziladigan holat. Har bir qo'shimcha o'tish GPU vaqtini yo'qotishdir. Kamaytirish usullari: theme da windowBackground ni o'rnating (layout da fon chizmang), canvas.clipRect() dan foydalaning, ichma-ich fonlarni birlashtirishdan saqlaning, ichma-ich LinearLayout o'rniga ConstraintLayout qo'llang. Android Studio → Profile GPU Rendering → Overdraw overdraw rang xaritasini ko'rsatadi (ko'k = 1x, qizil = 3x+).

Maxsus View da super.onDraw() kerakmi?

Ha, agar View fon (background) ga ega bo'lsa. super.onDraw() View fonini chizadi. Agar maxsus View ning foni bo'lmasa yoki o'z foningizni chizsangiz, super.onDraw() chaqirilmasligi mumkin — bu bitta chizish o'tishini tejaydi. ViewGroup uchun super.dispatchDraw() majburiydir — bola View-larni chizadi.

Xulosa

  • View Lifecycle — uch chizish fazasi: onMeasure (o'lchamlar), onLayout (pozitsiya), onDraw (chizish).
  • onMeasure MeasureSpec (EXACTLY, AT_MOST, UNSPECIFIED) ni qayta ishlaydi va setMeasuredDimension chaqiradi.
  • onLayout ViewGroup da bola View-larni left/top/right/bottom koordinatalari bilan joylashtiradi.
  • onDraw mazmunni Canvas da chizadi — bu metod ichida obyekt yaratmang.
  • invalidate() faqat onDraw chaqiradi, requestLayout() — to'liq tsikl onMeasure → onLayout → onDraw.
  • Maxsus View-lar UI-ni 15–40% tezlashtiradi, ammo onMeasure ni to'g'ri amalga oshirish va onDraw da obyekt keshlash talab qiladi.
  • Murakkab grafika uchun Hardware Acceleration dan foydalaning va Path/Bitmap keshlang.

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.

Loyihani muhokama qilish

Shuningdek o'qing