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 را ۱۵–۴۰٪ سریع‌تر می‌کنند در مقایسه با ترکیب 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 دارد) و onDetachedFromWindow (View جدا شده، منابع آزاد می‌شوند). این متدها یک بار در طول عمر View فراخوانی می‌شوند و برای ثبت/لغو انیمیشن‌ها، سنسورها مهم هستند.

طبق Android Performance Blog، ۶۵٪ مشکلات عملکرد UI (jank، افت فریم) به پیاده‌سازی نادرست onMeasure و onDraw مربوط است: override بیش از حد، فراخوانی requestLayout() بدون نیاز، ایجاد اشیاء در onDraw.

onMeasure: اندازه‌گیری ابعاد View

onMeasure — مهم‌ترین و پیچیده‌ترین فاز View Lifecycle. در این مرحله Android تعیین می‌کند که View چقدر فضای روی صفحه اشغال می‌کند. سیستم MeasureSpec — دستورالعمل‌های بسته‌بندی شده در int متشکل از حالت و اندازه را ارسال می‌کند.

سه حالت MeasureSpec:

حالتثابتمقدارمثال
EXACTLYMeasureSpec.EXACTLYاندازه دقیق تعیین شده توسط والد (match_parent یا عرض ثابت)width=400dp → MeasureSpec(400, EXACTLY)
AT_MOSTMeasureSpec.AT_MOSTView می‌تواند تا حداکثر مشخص شده اندازه باشد (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 — اندازه را بر اساس محتوا (متن، تصویر) محاسبه کند.
  • داخل onMeasure requestLayout() فراخوانی نکند — این باعث حلقه بی‌نهایت می‌شود.

خطای رایج: عدم در نظر گرفتن 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 نسبت به layout قبلی تغییر کرده است. اگر false باشد — View می‌تواند برای بهینه‌سازی محاسبه مجدد موقعیت فرزندان را نادیده بگیرد.

برای ViewGroup onLayout باید:

  • همه فرزندان را از طریق getChildCount() و getChildAt(i) پیمایش کند.
  • برای هر فرزند left، top، right، bottom — مختصات درون ViewGroup (با در نظر گرفتن padding) را تعیین کند.
  • child.layout(l, t, r, b) را برای هر فرزند فراخوانی کند.
  • gravity، margins، alignment را در نظر بگیرد.

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 و افت فریم می‌شود.
  • درون onDraw requestLayout() یا invalidate() فراخوانی نکنید — این باعث حلقه بی‌نهایت بازنمایی می‌شود.
  • محاسبات طولانی انجام ندهید — onDraw روی UI thread اجرا می‌شود. محاسبات پیچیده باید به thread پس‌زمینه منتقل یا از پیش محاسبه شوند.
  • از Hardware Acceleration استفاده کنید — از API 14+ Canvas می‌تواند از طریق GPU کار کند. برای گرافیک پیچیده (گرادیان‌ها، سایه‌ها، چرخش‌ها) شتاب HW عملکرد را تا ۳۰۰٪ افزایش می‌دهد.
  • فقط ناحیه قابل مشاهده را رسم کنید — از canvas.clipRect() برای برش بخش‌های نامرئی استفاده کنید.

ترتیب رسم در ViewGroup: پس‌زمینه (setBackgroundDrawable) → onDraw (محتوا) → dispatchDraw (Viewهای فرزند) → onDrawForeground (پیش‌زمینه). dispatchDraw onDraw هر فرزند را فراخوانی می‌کند. override dispatchDraw برای اعمال افکت‌ها روی عناصر فرزند استفاده می‌شود.

طبق آمار Android Vitals، شایع‌ترین دلایل افت فریم در onDraw — ایجاد اشیاء درون متد (۴۸٪)، فراخوانی decodeResource (۲۲٪) و عملیات پیچیده با Path بدون کش (۱۵٪).

Invalidation: زمانی که View بازنمایی می‌شود

Invalidation — مکانیزمی که بازنمایی View را راه‌اندازی می‌کند. فراخوانی invalidate() View را به عنوان «کثیف» علامت‌گذاری می‌کند و فراخوانی onDraw را در چرخه بعدی رسم برنامه‌ریزی می‌کند. فراخوانی requestLayout() — عملیات «سنگین‌تری» است که چرخه کامل را راه‌اندازی می‌کند: onMeasure → onLayout → onDraw.

متدچه کاری انجام می‌دهدچه زمانی استفاده کنیم
invalidate()onDraw را بدون onMeasure/onLayout فراخوانی می‌کندفقط ظاهر تغییر کرده است (رنگ، متن، پیشرفت)
invalidate(Rect)فقط ناحیه مشخص شده را بازنمایی می‌کندبخشی از View تغییر کرده — انیمیشن، انتخاب
postInvalidate()invalidate را از thread غیر UI فراخوانی می‌کندthread پس‌زمینه داده‌های رسم را به‌روز کرده است
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 با ۳–۵ View تو در تو هستند. Google بیش از ۱۰ View تو در تو در یک صفحه را توصیه نمی‌کند.
  • برای سلسله‌مراتب مسطح از ConstraintLayout استفاده کنید — یک ViewGroup واحد با عملکرد نزدیک به RelativeLayout می‌سازد، اما بدون تودرتویی.
  • لایه سخت‌افزاری را هنگام بازنمایی غیرفعال کنید — برای Viewهای دارای انیمیشن از setLayerType(LAYER_TYPE_HARDWARE) و پس از اتمام از setLayerType(LAYER_TYPE_NONE) استفاده کنید.
  • از invalidate() با Rect استفاده کنید — فقط ناحیه تغییر یافته را بازنمایی کنید، نه کل View را.
  • از overdraw اجتناب کنید — از Profile GPU Rendering در Android Studio برای تشخیص رسم‌های اضافی استفاده کنید. میانگین overdraw برای برنامه‌های Google — 1.5x، حداکثر توصیه شده — 2.5x.

نمونه‌های کد View در Kotlin

مثال ۱: 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.

مثال ۲: 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 را override می‌کند: هر فرزند را اندازه‌گیری می‌کند، هنگام تجاوز از عرض به ردیف جدید منتقل می‌کند، ارتفاع کل را محاسبه می‌کند. onLayout کودکان را با مختصات با در نظر گرفتن انتقال خطوط قرار می‌دهد. generateLayoutParams برای پشتیبانی از margin در Viewهای فرزند، MarginLayoutParams را برمی‌گرداند.

مثال ۳: 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 به طور متوسط ۲–۵ میلی‌ثانیه در ViewGroup با ۱۰ عنصر زمان می‌برد. برای انیمیشن‌ها از invalidate() استفاده کنید.

چه زمانی onAttachedToWindow فراخوانی می‌شود؟

onAttachedToWindow زمانی فراخوانی می‌شود که View به پنجره (Window) متصل می‌شود — بخشی از سلسله‌مراتب قابل مشاهده می‌شود. در این لحظه View شتاب HW و دسترسی به منابع Window (WindowManager، Display) را دریافت می‌کند. onAttachedToWindow مکان مناسبی برای ثبت شنوندگان انیمیشن و BroadcastReceiver است که تا زمانی که View قابل مشاهده است زندگی می‌کند.

overdraw چیست و چگونه آن را کاهش دهیم؟

Overdraw — وضعیتی است که در آن یک پیکسل چندین بار در یک فریم رسم می‌شود. هر عبور اضافی اتلاف زمان GPU است. روش‌های کاهش: windowBackground را در theme تنظیم کنید (در layout پس‌زمینه رسم نکنید)، از canvas.clipRect() استفاده کنید، از ادغام پس‌زمینه‌های تو در تو خودداری کنید، به جای LinearLayoutهای تو در تو از ConstraintLayout استفاده کنید. 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 را ۱۵–۴۰٪ سریع‌تر می‌کنند، اما نیاز به پیاده‌سازی صحیح onMeasure و کش اشیاء در onDraw دارند.
  • برای گرافیک پیچیده از Hardware Acceleration استفاده کنید و Path/Bitmap را کش کنید.

ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد

IT Sectr از سال 2017 برنامه‌های iOS و Android را برای استارتاپ‌ها و کسب‌وکارها ایجاد می‌کند. ما به شما مشاوره می‌دهیم و بهترین راه‌حل را پیشنهاد خواهیم کرد.

بحث درباره پروژه

همچنین بخوانید