View Lifecycle — τι είναι, διαδικασίες onMeasure onLayout onDraw

Συγγραφέας: IT Sectr Δημοσιεύτηκε: 2026-03-05 Χρόνος ανάγνωσης: 10 λεπ

View Lifecycle — η ακολουθία μεθόδων που καλεί το Android για την απόδοση και επαναπόδοση ενός στοιχείου διεπαφής χρήστη (View) στην οθόνη. Σε αντίθεση με το Activity ή το Fragment, το View είναι ένα ελαφρύ στοιχείο που δεν έχει εκτεταμένο κύκλο ζωής, αλλά περνά από μια αυστηρή τριφασική διαδικασία: onMeasure (μέτρηση), onLayout (τοποθέτηση), onDraw (σχεδίαση). Η κατανόηση του View Lifecycle είναι απαραίτητη για τη δημιουργία προσαρμοσμένων View, τη βελτιστοποίηση απόδοσης και την επίλυση προβλημάτων απόδοσης. Σύμφωνα με την Google, τα προσαρμοσμένα View επιταχύνουν το UI κατά 15–40% σε σύγκριση με τον συνδυασμό τυπικών ένθετων ViewGroup όταν εφαρμόζονται σωστά. Η τεκμηρίωση Android για προσαρμοσμένα View περιγράφει τα onMeasure, onLayout και onDraw ως τους τρεις πυλώνες του View Lifecycle.

Κύρια σημεία

  • View Lifecycle αποτελείται από τρεις φάσεις: onMeasure (διαστάσεις), onLayout (θέσεις), onDraw (σχεδίαση) — και ενεργοποιείται με invalidate() ή requestLayout().
  • Το onMeasure υπολογίζει το πλάτος και το ύψος του View βάσει του MeasureSpec (AT_MOST, EXACTLY, UNSPECIFIED).
  • Το onLayout τοποθετεί τα θυγατρικά View μέσα στο ViewGroup, καθορίζοντας τις συντεταγμένες left, top, right, bottom.
  • Το onDraw αποδίδει το περιεχόμενο του View στο Canvas: φόντο, κείμενο, σχήματα, εικόνες.
  • Λανθασμένο View Lifecycle — κύρια αιτία προβλημάτων απόδοσης UI (jank, dropped frames) και ιεραρχίας.

View Lifecycle — τι είναι στο Android

View Lifecycle — η διαδικασία που ακολουθεί το Android View (και ViewGroup) για να εμφανιστεί στην οθόνη. Σε αντίθεση με το Activity ή το Fragment, το View δεν έχει onStart/onStop/onDestroy — η "ζωή" του αποτελείται από μια κυκλική διαδικασία μέτρησης, τοποθέτησης και σχεδίασης. Αυτός ο κύκλος ενεργοποιείται κάθε φορά που το View πρέπει να εμφανιστεί ή να επανασχεδιαστεί.

Τρεις φάσεις του View Lifecycle:

  • onMeasure(int widthMeasureSpec, int heightMeasureSpec) — καθορίζει τις επιθυμητές διαστάσεις του View. Το σύστημα μεταβιβάζει το MeasureSpec — μια οδηγία σχετικά με τις επιτρεπόμενες διαστάσεις (ακριβής τιμή, μέγιστη ή χωρίς περιορισμό).
  • onLayout(boolean changed, int left, int top, int right, int bottom) — τοποθετεί το View και τους απογόνους του στην οθόνη. Για το View καθορίζει τα δικά του όρια, για το ViewGroup — τις θέσεις των θυγατρικών στοιχείων.
  • onDraw(Canvas canvas) — σχεδιάζει το περιεχόμενο του View στο παρεχόμενο Canvas. Το σύστημα παρέχει ένα Canvas που μεταφράζει τις εντολές σε bitmap ή GPU.

Ο πλήρης κύκλος του View Lifecycle περιλαμβάνει επίσης μεθόδους που σχετίζονται με την προσάρτηση του View στο παράθυρο: onAttachedToWindow (το View είναι προσαρτημένο στο παράθυρο, έχει HW acceleration) και onDetachedFromWindow (το View αποσπάται, οι πόροι ελευθερώνονται). Αυτές οι μέθοδοι καλούνται μία φορά στη ζωή του View και είναι σημαντικές για την εγγραφή/ακύρωση κινούμενων σχεδίων, αισθητήρων.

Σύμφωνα με το Android Performance Blog, το 65% των προβλημάτων απόδοσης UI (jank, χαμένα καρέ) σχετίζονται με λανθασμένη υλοποίηση των onMeasure και onDraw: υπερβολική παράκαμψη, κλήση requestLayout() χωρίς ανάγκη, δημιουργία αντικειμένων στο onDraw.

onMeasure: μέτρηση διαστάσεων View

onMeasure — η πιο σημαντική και πιο περίπλοκη φάση του View Lifecycle. Σε αυτό το στάδιο το Android καθορίζει πόσο χώρο θα καταλάβει το View στην οθόνη. Το σύστημα μεταβιβάζει το MeasureSpec — οδηγίες συσκευασμένες σε int που αποτελούνται από λειτουργία και μέγεθος.

Τρεις λειτουργίες MeasureSpec:

ΛειτουργίαΣταθεράΣημασίαΠαράδειγμα
EXACTLYMeasureSpec.EXACTLYΑκριβές μέγεθος που καθορίζεται από τον γονέα (match_parent ή σταθερό πλάτος)width=400dp → MeasureSpec(400, EXACTLY)
AT_MOSTMeasureSpec.AT_MOSTΤο View μπορεί να έχει μέγεθος έως το καθορισμένο μέγιστο (wrap_content)width ≤ 400dp → MeasureSpec(400, AT_MOST)
UNSPECIFIEDMeasureSpec.UNSPECIFIEDΧωρίς περιορισμούς — το View μπορεί να έχει οποιοδήποτε μέγεθος (ScrollView, RecyclerView)πλάτος απεριόριστο → MeasureSpec(0, UNSPECIFIED)

Η υλοποίηση του onMeasure πρέπει:

  • Να καλέσει setMeasuredDimension(int width, int height) για αποθήκευση των μετρημένων διαστάσεων.
  • Να λάβει υπόψη το padding — να αφαιρέσει getPaddingLeft() + getPaddingRight() από το διαθέσιμο πλάτος.
  • Για ViewGroup — να μετρήσει όλους τους απογόνους μέσω measureChild() ή measureChildWithMargins().
  • Για wrap_content — να υπολογίσει το μέγεθος βάσει περιεχομένου (κείμενο, εικόνα).
  • Να μην καλεί requestLayout() μέσα στο onMeasure — αυτό θα προκαλέσει ατελείωτο βρόχο.

Τυπικό σφάλμα: μη λήψη υπόψη του MeasureSpec στο wrap_content. Εάν το View έχει οριστεί σε wrap_content, αλλά το onMeasure δεν επεξεργάζεται το AT_MOST και επιστρέφει σταθερό μέγεθος, το View είτε θα κοπεί είτε θα καταλάβει περισσότερο χώρο από όσο χρειάζεται.

onLayout: τοποθέτηση View στην οθόνη

onLayout — η φάση κατά την οποία το View ή ViewGroup τοποθετεί τους απογόνους του εντός των ορίων του. Για ένα συνηθισμένο View (όχι ViewGroup) το onLayout δεν απαιτείται — το σύστημα καλεί το layout() με παραμέτρους από τον γονέα. Για το ViewGroup το onLayout είναι υποχρεωτικό — χωρίς αυτό τα θυγατρικά View δεν θα τοποθετηθούν.

Υπογραφή onLayout:

java
@Override
protected void onLayout(boolean changed,
        int left, int top,
        int right, int bottom) {
    // τοποθέτηση θυγατρικών View
}

Η παράμετρος changed υποδεικνύει εάν η θέση ή το μέγεθος του View έχει αλλάξει σε σύγκριση με την προηγούμενη διάταξη. Εάν είναι false — το View μπορεί να παραλείψει τον επανυπολογισμό θέσεων απογόνων για βελτιστοποίηση.

Για το ViewGroup το onLayout πρέπει:

  • Να διατρέξει όλους τους απογόνους μέσω getChildCount() και getChildAt(i).
  • Για κάθε απόγονο να καθορίσει left, top, right, bottom — συντεταγμένες εντός ViewGroup (με padding).
  • Να καλέσει child.layout(l, t, r, b) για κάθε απόγονο.
  • Να λάβει υπόψη gravity, περιθώρια, στοίχιση.

Το onLayout καλείται μετά το onMeasure — οι μετρημένες διαστάσεις είναι διαθέσιμες μέσω getMeasuredWidth()/getMeasuredHeight(). Εάν ένα θυγατρικό View έχει διαφορετικές πραγματικές διαστάσεις μετά το layout(), θα κληθεί requestLayout() για επαναμέτρηση. Αυτό ονομάζεται "layout pass" και μπορεί να προκαλέσει αλυσιδωτή αντίδραση επανυπολογισμών.

onDraw: σχεδίαση περιεχομένου Canvas

onDraw — η φάση κατά την οποία το View σχεδιάζει τον εαυτό του στο Canvas. Είναι η μόνη φάση που μπορεί να κληθεί πολλές φορές χωρίς onMeasure και onLayout — εάν το View επισημανθεί ως invalidate(). Το Canvas παρέχει API για σχεδίαση: drawLine, drawRect, drawCircle, drawText, drawBitmap και drawPath.

Κανόνες onDraw:

  • Μην δημιουργείτε αντικείμενα στο onDraw — κάθε κλήση onDraw πρέπει να χρησιμοποιεί προδημιουργημένα αντικείμενα (Path, Paint, Rect). Η δημιουργία αντικειμένων στο onDraw προκαλεί παύσεις GC και χαμένα καρέ.
  • Μην καλείτε requestLayout() ή invalidate() μέσα στο onDraw — αυτό θα ενεργοποιήσει έναν ατελείωτο βρόχο επανασχεδίασης.
  • Μην κάνετε μεγάλους υπολογισμούς — το onDraw εκτελείται στο νήμα UI. Οι πολύπλοκοι υπολογισμοί πρέπει να μεταφερθούν σε νήμα παρασκηνίου ή να υπολογιστούν εκ των προτέρων.
  • Χρησιμοποιήστε Hardware Acceleration — από το API 14+ το Canvas μπορεί να λειτουργήσει μέσω GPU. Για σύνθετα γραφικά (διαβαθμίσεις, σκιές, περιστροφές) η επιτάχυνση HW δίνει αύξηση απόδοσης έως 300%.
  • Σχεδιάστε μόνο την ορατή περιοχή — χρησιμοποιήστε canvas.clipRect() για να κόψετε τα αόρατα μέρη.

Σειρά σχεδίασης στο ViewGroup: φόντο (setBackgroundDrawable) → onDraw (περιεχόμενο) → dispatchDraw (θυγατρικά View) → onDrawForeground (προσκήνιο). Το dispatchDraw καλεί το onDraw κάθε απογόνου. Η παράκαμψη του dispatchDraw χρησιμοποιείται για εφαρμογή εφέ πάνω από θυγατρικά στοιχεία.

Σύμφωνα με στατιστικά του Android Vitals, οι συχνότερες αιτίες χαμένων καρέ στο onDraw — δημιουργία αντικειμένων εντός της μεθόδου (48%), κλήση decodeResource (22%) και σύνθετες λειτουργίες με Path χωρίς προσωρινή αποθήκευση (15%).

Invalidation: πότε το View επανασχεδιάζεται

Invalidation — ο μηχανισμός που ενεργοποιεί την επανασχεδίαση του View. Η κλήση invalidate() επισημαίνει το View ως "βρώμικο" και προγραμματίζει την κλήση onDraw στον επόμενο κύκλο σχεδίασης. Η κλήση requestLayout() — μια πιο "βαριά" λειτουργία που ενεργοποιεί τον πλήρη κύκλο: onMeasure → onLayout → onDraw.

ΜέθοδοςΤι κάνειΠότε να χρησιμοποιείται
invalidate()Καλεί το onDraw χωρίς onMeasure/onLayoutΆλλαξε μόνο η εμφάνιση (χρώμα, κείμενο, πρόοδος)
invalidate(Rect)Επανασχεδιάζει μόνο την καθορισμένη περιοχήΆλλαξε μέρος του View — κινούμενο σχέδιο, επιλογή
postInvalidate()Καλεί invalidate από μη UI νήμαΤο νήμα παρασκηνίου ενημέρωσε δεδομένα για σχεδίαση
requestLayout()Ενεργοποιεί onMeasure → onLayout → onDrawΆλλαξε το μέγεθος περιεχομένου (κείμενο, εικόνα)
forceLayout()Επισημαίνει View για υποχρεωτική επαναμέτρησηΗ εσωτερική κατάσταση άλλαξε, το μέγεθος μπορεί να άλλαξε

Κινούμενα σχέδια και View Lifecycle: Τα ViewPropertyAnimator και ValueAnimator καλούν invalidate() σε κάθε καρέ κινούμενου σχεδίου. Το ObjectAnimator καλεί setter στο View που, εάν ο setter αλλάζει το μέγεθος (width/height), καλεί αυτόματα requestLayout(). Αυτό μπορεί να είναι δαπανηρό για σύνθετα ViewGroup: κάθε requestLayout ενεργοποιεί ολόκληρη την ιεραρχία έως το ριζικό view.

Κανόνας βελτιστοποίησης: invalidate() αντί για requestLayout() παντού όπου αλλάζει μόνο η εμφάνιση (χρώμα, διαφάνεια, περιστροφή χωρίς αλλαγή μεγέθους). Χρησιμοποιήστε requestLayout μόνο όταν αλλάζουν οι διαστάσεις ή το περιεχόμενο που επηρεάζει το μέγεθος.

Βελτιστοποίηση προσαρμοσμένων View: βέλτιστες πρακτικές

Τα προσαρμοσμένα View — ισχυρό εργαλείο για τη δημιουργία μοναδικού UI, αλλά απαιτούν αυστηρή τήρηση των κανόνων απόδοσης. Ακολουθούν οι βασικές συστάσεις της Google για βελτιστοποίηση του View Lifecycle.

  • Υπολογίστε εκ των προτέρων ό,τι μπορεί να υπολογιστεί — διαστάσεις, συντεταγμένες, διαδρομή Path, χρώματα διαβάθμισης. Στο onDraw χρησιμοποιήστε μόνο σχεδίαση.
  • Αποθηκεύστε προσωρινά το αποτέλεσμα μέτρησης — εάν το View έχει σταθερές διαστάσεις, αποθηκεύστε το MeasureSpec και επιστρέψτε setMeasuredDimension χωρίς επιπλέον υπολογισμούς.
  • Χρησιμοποιήστε ViewConfiguration — getScaledTouchSlop, getScaledMinimumFlingVelocity — για τον χειρισμό αφής.
  • Ελαχιστοποιήστε τον αριθμό View στην ιεραρχία — τα προσαρμοσμένα View που συνδυάζουν πολλαπλά στοιχεία είναι πάντα ταχύτερα από ένα ViewGroup με 3–5 ένθετα View. Η Google συνιστά όχι περισσότερα από 10 ένθετα View σε μία οθόνη.
  • Χρησιμοποιήστε ConstraintLayout για επίπεδη ιεραρχία — δημιουργεί ένα μόνο ViewGroup με απόδοση κοντά στο RelativeLayout, αλλά χωρίς ένθεση.
  • Απενεργοποιήστε το υλικό επίπεδο κατά την επανασχεδίαση — χρησιμοποιήστε setLayerType(LAYER_TYPE_HARDWARE) για View με κινούμενα σχέδια και setLayerType(LAYER_TYPE_NONE) μετά την ολοκλήρωση.
  • Χρησιμοποιήστε invalidate() με Rect — επανασχεδιάστε μόνο την περιοχή που άλλαξε, όχι ολόκληρο το View.
  • Αποφύγετε το overdraw — χρησιμοποιήστε Profile GPU Rendering στο Android Studio για ανίχνευση περιττών σχεδιάσεων. Μέσο overdraw για εφαρμογές Google — 1.5x, μέγιστο συνιστώμενο — 2.5x.

Παραδείγματα κώδικα View σε Kotlin

Παράδειγμα 1: Προσαρμοσμένο View — δείκτης προόδου

Απλός κυκλικός δείκτης προόδου με σωστή υλοποίηση onMeasure, onDraw και invalidate.

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

Κυκλική γραμμή προόδου: το onMeasure επιστρέφει τετράγωνο μέγεθος βάσει MeasureSpec, το onSizeChanged απομνημονεύει τις διαστάσεις, το onDraw σχεδιάζει το φόντο και το τόξο προόδου. Το Invalidate καλείται όταν αλλάζει η πρόοδος — τα onMeasure/onLayout δεν επηρεάζονται. Το Paint δημιουργείται μία φορά στον κατασκευαστή, όχι στο onDraw.

Παράδειγμα 2: ViewGroup — απλό FlowLayout

Προσαρμοσμένο ViewGroup που τοποθετεί τα θυγατρικά View σε σειρές (σαν Flexbox wrap).

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: μετρά κάθε απόγονο, μεταφέρει σε νέα σειρά όταν υπερβαίνει το πλάτος, υπολογίζει το συνολικό ύψος. Το onLayout τοποθετεί τα παιδιά βάσει συντεταγμένων λαμβάνοντας υπόψη τις αλλαγές σειρών. Η generateLayoutParams επιστρέφει MarginLayoutParams για υποστήριξη περιθωρίων στα θυγατρικά View.

Παράδειγμα 3: onDraw με προσωρινή αποθήκευση Path

Προσαρμοσμένο View σχεδιάζει ομαλή καμπύλη Bezier, υπολογίζοντας το Path εκ των προτέρων και αποθηκεύοντάς το προσωρινά.

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: το isPathDirty = true μόνο όταν αλλάζουν οι διαστάσεις του View ή καλείται η refreshWave(). Στο onDraw το Path επανυπολογίζεται μόνο εάν είναι "βρώμικο". Αυτό αποτρέπει τον επανυπολογισμό της καμπύλης Bezier σε κάθε καρέ κινούμενου σχεδίου, εξοικονομώντας CPU.

Συχνές ερωτήσεις

Σε τι διαφέρει το View Lifecycle από το Activity Lifecycle;

View Lifecycle — μια κυκλική διαδικασία σχεδίασης (onMeasure → onLayout → onDraw), ανεξάρτητη από τη δημιουργία/καταστροφή του Activity. Το View δεν έχει onStart/onStop — είτε είναι ορατό (προσαρτημένο στο παράθυρο) είτε όχι. Το Activity Lifecycle διαχειρίζεται την κατάσταση του στοιχείου εφαρμογής, το View Lifecycle — τη σχεδίαση του UI.

Πώς επηρεάζει το requestLayout() την απόδοση;

requestLayout() ενεργοποιεί τον πλήρη κύκλο onMeasure → onLayout → onDraw για ολόκληρο το δέντρο View από τη ρίζα. Εάν το requestLayout() καλείται συχνά (π.χ. κάθε καρέ κινούμενου σχεδίου), προκαλεί jank και χαμένα καρέ. Σύμφωνα με την Google, ένα requestLayout διαρκεί κατά μέσο όρο 2–5 ms σε ένα ViewGroup με 10 στοιχεία. Για κινούμενα σχέδια χρησιμοποιήστε invalidate().

Πότε καλείται το onAttachedToWindow;

onAttachedToWindow καλείται όταν το View προσαρτάται στο παράθυρο (Window) — γίνεται μέρος της ορατής ιεραρχίας. Αυτή τη στιγμή το View λαμβάνει HW acceleration και πρόσβαση στους πόρους του Window (WindowManager, Display). Το onAttachedToWindow είναι το κατάλληλο μέρος για εγγραφή ακροατών κινούμενων σχεδίων και BroadcastReceiver που ζει όσο το View είναι ορατό.

Τι είναι το overdraw και πώς να το μειώσετε;

Overdraw — κατάσταση κατά την οποία ένα εικονοστοιχείο σχεδιάζεται πολλές φορές σε ένα καρέ. Κάθε επιπλέον πέρασμα είναι σπατάλη χρόνου GPU. Μέθοδοι μείωσης: ορίστε windowBackground στο theme (μην σχεδιάζετε φόντο στο layout), χρησιμοποιήστε canvas.clipRect(), αποφύγετε τη συγχώνευση ένθετων φόντων, εφαρμόστε ConstraintLayout αντί για ένθετα LinearLayout. Android Studio → Profile GPU Rendering → Overdraw δείχνει χρωματικό χάρτη overdraw (μπλε = 1x, κόκκινο = 3x+).

Χρειάζεται super.onDraw() σε προσαρμοσμένο View;

Ναι, εάν το View έχει φόντο (background). Το super.onDraw() σχεδιάζει το φόντο του View. Εάν το προσαρμοσμένο View δεν έχει φόντο ή σχεδιάζετε το δικό σας φόντο, το super.onDraw() μπορεί να παραλειφθεί — αυτό εξοικονομεί ένα πέρασμα σχεδίασης. Για ViewGroup το super.dispatchDraw() είναι υποχρεωτικό — σχεδιάζει τα θυγατρικά View.

Σύνοψη

  • View Lifecycle — τρεις φάσεις σχεδίασης: onMeasure (διαστάσεις), onLayout (θέση), onDraw (σχεδίαση).
  • Το onMeasure επεξεργάζεται το MeasureSpec (EXACTLY, AT_MOST, UNSPECIFIED) και καλεί setMeasuredDimension.
  • Το onLayout στο ViewGroup τοποθετεί τα θυγατρικά View με συντεταγμένες left/top/right/bottom.
  • Το onDraw σχεδιάζει περιεχόμενο στο Canvas — μην δημιουργείτε αντικείμενα εντός αυτής της μεθόδου.
  • Το invalidate() καλεί μόνο onDraw, το requestLayout() — τον πλήρη κύκλο onMeasure → onLayout → onDraw.
  • Τα προσαρμοσμένα View επιταχύνουν το UI κατά 15–40%, αλλά απαιτούν σωστή υλοποίηση onMeasure και προσωρινή αποθήκευση αντικειμένων στο onDraw.
  • Για σύνθετα γραφικά χρησιμοποιήστε Hardware Acceleration και αποθηκεύστε προσωρινά Path/Bitmap.

Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση

Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.

Συζήτηση έργου

Διαβάστε επίσης