View Lifecycle — এটি কী, onMeasure onLayout onDraw প্রক্রিয়াসমূহ

লেখক: IT Sectr প্রকাশিত: 2026-03-05 পড়ার সময়: 10 মিনিট

View Lifecycle — পদ্ধতিগুলোর ক্রম যা Android স্ক্রিনে ইউজার ইন্টারফেস উপাদান (View) আঁকা এবং পুনরায় আঁকার জন্য কল করে। Activity বা Fragment-এর বিপরীতে, View একটি হালকা ওজনের উপাদান যার বিস্তৃত জীবনচক্র নেই, তবে এটি একটি কঠোর তিন-পর্যায়ের প্রক্রিয়ার মধ্য দিয়ে যায়: onMeasure (মাপ), onLayout (অবস্থান নির্ধারণ), onDraw (অঙ্কন)। কাস্টম Views তৈরি, পারফরম্যান্স অপটিমাইজ এবং অঙ্কন সমস্যা সমাধানের জন্য View Lifecycle বোঝা প্রয়োজন। Google-এর মতে, সঠিক বাস্তবায়নে স্ট্যান্ডার্ড নেস্টেড ViewGroups-এর সংমিশ্রণের তুলনায় কাস্টম Views UI-কে 15–40% গতি বাড়ায়কাস্টম Views-এ Android ডকুমেন্টেশন onMeasure, onLayout এবং onDraw-কে View Lifecycle-এর তিনটি স্তম্ভ হিসেবে বর্ণনা করে।

মূল বিষয়

  • View Lifecycle তিনটি পর্যায় নিয়ে গঠিত: onMeasure (আকার), onLayout (অবস্থান), onDraw (অঙ্কন) — এবং invalidate() বা requestLayout() দ্বারা ট্রিগার হয়।
  • onMeasure MeasureSpec (AT_MOST, EXACTLY, UNSPECIFIED)-এর উপর ভিত্তি করে View-এর প্রস্থ এবং উচ্চতা গণনা করে।
  • onLayout ViewGroup-এর ভেতরে চাইল্ড Views সাজায়, তাদের left, top, right, bottom স্থানাঙ্ক নির্ধারণ করে।
  • onDraw View-এর বিষয়বস্তু Canvas-এ রেন্ডার করে: পটভূমি, টেক্সট, আকার, ছবি।
  • ভুল View Lifecycle UI পারফরম্যান্স সমস্যা (jank, ড্রপড ফ্রেম) এবং শ্রেণিবিন্যাস সমস্যার প্রধান কারণ।

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) — প্রদত্ত Canvas-এ View-এর বিষয়বস্তু আঁকে। সিস্টেম একটি Canvas সরবরাহ করে যা কমান্ডকে bitmap বা GPU-তে অনুবাদ করে।

পূর্ণ View Lifecycle চক্রে View-কে উইন্ডোর সাথে সংযুক্ত করার সাথে সম্পর্কিত পদ্ধতিও অন্তর্ভুক্ত: onAttachedToWindow (View উইন্ডোর সাথে সংযুক্ত, HW ত্বরণ আছে) এবং onDetachedFromWindow (View বিচ্ছিন্ন, সম্পদ মুক্ত করা হয়)। এই পদ্ধতিগুলো View-এর জীবনে একবার কল করা হয় এবং অ্যানিমেশন/সেন্সর নিবন্ধন/বাতিল করার জন্য গুরুত্বপূর্ণ।

Android Performance Blog-এর মতে, UI পারফরম্যান্স সমস্যার (jank, ফ্রেম ড্রপ) 65% 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_MOSTView নির্দিষ্ট সর্বোচ্চ আকার পর্যন্ত হতে পারে (wrap_content)width ≤ 400dp → MeasureSpec(400, AT_MOST)
UNSPECIFIEDMeasureSpec.UNSPECIFIEDকোনও সীমাবদ্ধতা নেই — View যেকোনো আকারের হতে পারে (ScrollView, RecyclerView)প্রস্থ সীমাহীন → MeasureSpec(0, UNSPECIFIED)

onMeasure বাস্তবায়ন করা উচিত:

  • মাপা মাত্রা সংরক্ষণ করতে setMeasuredDimension(int width, int height) কল করা।
  • প্যাডিং বিবেচনা করা — উপলব্ধ প্রস্থ থেকে getPaddingLeft() + getPaddingRight() বাদ দেওয়া।
  • ViewGroup-এর জন্য — measureChild() বা measureChildWithMargins()-এর মাধ্যমে সব সন্তানকে মাপা।
  • wrap_content-এর জন্য — বিষয়বস্তু (টেক্সট, ছবি) অনুযায়ী আকার গণনা করা।
  • onMeasure-এর ভেতরে requestLayout() কল না করা — এটি অসীম লুপের কারণ হবে।

সাধারণ ভুল: wrap_content ব্যবহার করার সময় MeasureSpec বিবেচনা না করা। যদি View wrap_content-এ সেট করা থাকে, কিন্তু onMeasure AT_MOST হ্যান্ডেল না করে এবং একটি নির্দিষ্ট আকার ফেরত দেয়, তাহলে View হয় ক্লিপ হবে অথবা প্রয়োজনের চেয়ে বেশি জায়গা নেবে।

onLayout: স্ক্রিনে Views স্থাপন

onLayout — সেই পর্যায় যেখানে View বা ViewGroup তার সন্তানদের সাজায় নিজস্ব সীমানার ভেতরে। সাধারণ View-এর (ViewGroup নয়) জন্য, onLayout প্রয়োজন নেই — সিস্টেম প্যারেন্ট থেকে পাঠানো প্যারামিটারসহ layout() কল করে। ViewGroup-এর জন্য, onLayout বাধ্যতামূলক — এটি ছাড়া চাইল্ড Views স্থাপিত হবে না।

onLayout স্বাক্ষর:

java
@Override
protected void onLayout(boolean changed,
        int left, int top,
        int right, int bottom) {
    // চাইল্ড Views সাজানো
}

changed প্যারামিটার নির্দেশ করে View-এর অবস্থান বা আকার পূর্ববর্তী layout-এর তুলনায় পরিবর্তিত হয়েছে কিনা। যদি false হয়, View অপটিমাইজেশনের জন্য চাইল্ড অবস্থানের পুনর্গণনা এড়িয়ে যেতে পারে।

ViewGroup-এর জন্য, onLayout করা উচিত:

  • getChildCount() এবং getChildAt(i)-এর মাধ্যমে সব সন্তানের উপর পুনরাবৃত্তি করা।
  • প্রত্যেক সন্তানের জন্য left, top, right, bottom নির্ধারণ করা — ViewGroup-এর ভেতরে স্থানাঙ্ক (প্যাডিং বিবেচনা করে)।
  • প্রত্যেক সন্তানের জন্য child.layout(l, t, r, b) কল করা।
  • গ্র্যাভিটি, মার্জিন, অ্যালাইনমেন্ট বিবেচনা করা।

onMeasure-এর পরে onLayout কল করা হয় — মাপা মাত্রা getMeasuredWidth()/getMeasuredHeight()-এর মাধ্যমে উপলব্ধ। যদি layout()-এর পরে কোনো চাইল্ড View-এর প্রকৃত মাত্রা ভিন্ন হয়, তাহলে পুনরায় মাপার জন্য requestLayout() কল করা হবে। এটিকে “layout পাস” বলা হয় এবং এটি পুনর্গণনার চেইন প্রতিক্রিয়া ট্রিগার করতে পারে।

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 থ্রেডে চলে। জটিল গণনা ব্যাকগ্রাউন্ড থ্রেডে সরানো বা পূর্ব-গণনা করা উচিত।
  • হার্ডওয়্যার ত্বরণ ব্যবহার করুন — API 14+ থেকে, Canvas GPU-এর মাধ্যমে কাজ করতে পারে। জটিল গ্রাফিক্স (গ্রেডিয়েন্ট, ছায়া, ঘূর্ণন) জন্য, HW ত্বরণ 300% পর্যন্ত পারফরম্যান্স উন্নতি দেয়।
  • শুধুমাত্র দৃশ্যমান এলাকা আঁকুন — অদৃশ্য অংশ ক্লিপ করতে canvas.clipRect() ব্যবহার করুন।

ViewGroup-এ অঙ্কন ক্রম: পটভূমি (setBackgroundDrawable) → onDraw (বিষয়বস্তু) → dispatchDraw (চাইল্ড Views) → onDrawForeground (অগ্রভূমি)। dispatchDraw প্রতিটি চাইল্ডের onDraw কল করে। dispatchDraw ওভাররাইড করা চাইল্ড উপাদানের উপরে প্রভাব প্রয়োগ করতে ব্যবহৃত হয়।

Android Vitals পরিসংখ্যান অনুসারে, onDraw-এ ফ্রেম ড্রপের সবচেয়ে সাধারণ কারণ হল পদ্ধতির ভেতরে অবজেক্ট তৈরি (48%), decodeResource কল (22%), এবং ক্যাশিং ছাড়া জটিল Path অপারেশন (15%)।

অবৈধকরণ: কখন View পুনরায় আঁকা হয়

অবৈধকরণ — সেই প্রক্রিয়া যা View পুনরাঙ্কন ট্রিগার করেinvalidate() কল করা View-কে “নোংরা” হিসেবে চিহ্নিত করে এবং পরবর্তী অঙ্কন চক্রে onDraw কল নির্ধারণ করে। requestLayout() কল করা আরও “ভারী” অপারেশন, যা পূর্ণ চক্র ট্রিগার করে: onMeasure → onLayout → onDraw।

পদ্ধতিএটি কী করেকখন ব্যবহার করবেন
invalidate()onMeasure/onLayout ছাড়া onDraw ট্রিগার করেশুধুমাত্র চেহারা বদলালে (রং, টেক্সট, অগ্রগতি)
invalidate(Rect)শুধুমাত্র নির্দিষ্ট এলাকা পুনরায় আঁকেView-এর অংশ বদলালে — অ্যানিমেশন, নির্বাচন
postInvalidate()অ-UI থ্রেড থেকে invalidate কল করেব্যাকগ্রাউন্ড থ্রেড অঙ্কনের জন্য ডেটা আপডেট করলে
requestLayout()onMeasure → onLayout → onDraw ট্রিগার করেবিষয়বস্তুর আকার বদলালে (টেক্সট, ছবি)
forceLayout()বলপূর্বক পুনর্মাপনের জন্য View চিহ্নিত করেঅভ্যন্তরীণ অবস্থা বদলালে, আকার বদলাতে পারে

অ্যানিমেশন এবং View Lifecycle: ViewPropertyAnimator এবং ValueAnimator প্রতিটি অ্যানিমেশন ফ্রেমে invalidate() কল করে। ObjectAnimator View-এ setter কল করে, যা যদি setter আকার (প্রস্থ/উচ্চতা) বদলায়, স্বয়ংক্রিয়ভাবে requestLayout() কল করে। এটি জটিল ViewGroups-এর জন্য ব্যয়বহুল হতে পারে: প্রতিটি requestLayout রুট ভিউ পর্যন্ত পূর্ণ শ্রেণিবিন্যাস ট্রিগার করে।

অপটিমাইজেশন নিয়ম: যেখানে শুধুমাত্র চেহারা বদলায় (রং, স্বচ্ছতা, আকার না বদলে ঘূর্ণন) সেখানে requestLayout()-এর পরিবর্তে invalidate() ব্যবহার করুন। requestLayout শুধুমাত্র আকার বা বিষয়বস্তু পরিবর্তন করার সময় ব্যবহার করুন যা আকারকে প্রভাবিত করে।

কাস্টম Views অপটিমাইজেশন: সর্বোত্তম অনুশীলন

কাস্টম Views অনন্য UI তৈরি করার জন্য একটি শক্তিশালী হাতিয়ার, তবে তাদের পারফরম্যান্স নিয়ম কঠোরভাবে মেনে চলতে হবে। View Lifecycle অপটিমাইজেশনের জন্য Google-এর মূল সুপারিশ এখানে দেওয়া হল।

  • যা কিছু গণনা করা যায় তা পূর্ব-গণনা করুন — আকার, স্থানাঙ্ক, পথ, গ্রেডিয়েন্ট রং। onDraw-এ, শুধুমাত্র অঙ্কন করুন।
  • মাপার ফলাফল ক্যাশ করুন — যদি View-এর নির্দিষ্ট মাত্রা থাকে, MeasureSpec সংরক্ষণ করুন এবং অতিরিক্ত গণনা ছাড়া setMeasuredDimension ফেরত দিন।
  • ViewConfiguration ব্যবহার করুন — getScaledTouchSlop, getScaledMinimumFlingVelocity — স্পর্শ হ্যান্ডলিংয়ের জন্য।
  • শ্রেণিবিন্যাসে Views-এর সংখ্যা কমান — কাস্টম Views যা একাধিক উপাদান একত্রিত করে, সবসময় 3–5 নেস্টেড Views-যুক্ত ViewGroup-এর চেয়ে দ্রুত। Google প্রতি স্ক্রিনে ১০টির বেশি নেস্টেড View সুপারিশ করে না।
  • সমতল শ্রেণিবিন্যাসের জন্য ConstraintLayout ব্যবহার করুন — এটি RelativeLayout-এর কাছাকাছি পারফরম্যান্স সহ একটি একক ViewGroup তৈরি করে, কিন্তু নেস্টিং ছাড়া।
  • পুনরাঙ্কনের পরে হার্ডওয়্যার লেয়ার বন্ধ করুন — অ্যানিমেশনযুক্ত Views-এর জন্য setLayerType(LAYER_TYPE_HARDWARE) এবং শেষে setLayerType(LAYER_TYPE_NONE) ব্যবহার করুন।
  • Rect-এর সাথে invalidate() ব্যবহার করুন — পুরো View-এর পরিবর্তে শুধুমাত্র পরিবর্তিত এলাকা পুনরায় আঁকুন।
  • ওভারড্র এড়িয়ে চলুন — অপ্রয়োজনীয় পুনরাঙ্কন সনাক্ত করতে Android Studio-তে Profile GPU Rendering ব্যবহার করুন। Google অ্যাপের জন্য গড় ওভারড্র 1.5x, সর্বোচ্চ সুপারিশকৃত 2.5x।

Kotlin-এ View কোড উদাহরণ

উদাহরণ 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 যা চাইল্ড Viewsকে সারিতে সাজায় (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 চাইল্ড Views-এ মার্জিন সমর্থনের জন্য MarginLayoutParams ফেরত দেয়।

উদাহরণ 3: Path ক্যাশিং সহ onDraw

একটি কাস্টম 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() মূল থেকে সম্পূর্ণ View ট্রির জন্য পূর্ণ onMeasure → onLayout → onDraw চক্র ট্রিগার করে। যদি requestLayout() ঘন ঘন কল করা হয় (যেমন, প্রতি অ্যানিমেশন ফ্রেম), তাহলে এটি jank এবং ড্রপড ফ্রেম ঘটায়। Google-এর মতে, ১০টি উপাদানের ViewGroup-এ একটি requestLayout গড়ে 2–5 ms নেয়। অ্যানিমেশনের জন্য, invalidate() ব্যবহার করুন।

কখন onAttachedToWindow কল করা হয়?

onAttachedToWindow কল করা হয় যখন View একটি Window-এর সাথে সংযুক্ত হয় — দৃশ্যমান শ্রেণিবিন্যাসের অংশ হয়। এই মুহূর্তে, View HW ত্বরণ এবং Window সম্পদ (WindowManager, Display)-এ অ্যাক্সেস পায়। onAttachedToWindow অ্যানিমেশন শ্রোতা এবং BroadcastReceivers নিবন্ধনের সঠিক জায়গা যা View দৃশ্যমান থাকা পর্যন্ত জীবিত থাকে।

ওভারড্র কী এবং কীভাবে এটি কমানো যায়?

ওভারড্র হল এমন একটি অবস্থা যখন একটি পিক্সেল একক ফ্রেমে একাধিকবার আঁকা হয়। প্রতিটি অতিরিক্ত পাস GPU সময় নষ্ট করে। কমানোর পদ্ধতি: থিমে windowBackground সেট করুন (layout-এ পটভূমি আঁকবেন না), canvas.clipRect() ব্যবহার করুন, নেস্টেড পটভূমি একত্রিত করা এড়িয়ে চলুন, নেস্টেড LinearLayout-এর পরিবর্তে ConstraintLayout ব্যবহার করুন। Android Studio → Profile GPU Rendering → Overdraw ওভারড্রের রঙ মানচিত্র দেখায় (নীল = 1x, লাল = 3x+)।

কাস্টম View-এ super.onDraw() প্রয়োজন?

হ্যাঁ, যদি View-এর পটভূমি থাকে। super.onDraw() View-এর পটভূমি আঁকে। যদি আপনার কাস্টম View-এর পটভূমি না থাকে বা আপনি নিজস্ব পটভূমি আঁকেন, তাহলে super.onDraw() বাদ দেওয়া যেতে পারে — এটি একটি অঙ্কন পাস বাঁচায়। ViewGroup-এর জন্য, super.dispatchDraw() বাধ্যতামূলক — এটি চাইল্ড Views আঁকে।

সারসংক্ষেপ

  • View Lifecycle — তিনটি অঙ্কন পর্যায়: onMeasure (মাত্রা), onLayout (অবস্থান), onDraw (রেন্ডারিং)।
  • onMeasure MeasureSpec (EXACTLY, AT_MOST, UNSPECIFIED) প্রক্রিয়া করে এবং setMeasuredDimension কল করে।
  • ViewGroup-এ onLayout চাইল্ড Views-কে left/top/right/bottom স্থানাঙ্কে অবস্থান করে।
  • onDraw Canvas-এ বিষয়বস্তু রেন্ডার করে — এই পদ্ধতির ভেতরে অবজেক্ট তৈরি করবেন না।
  • invalidate() শুধুমাত্র onDraw ট্রিগার করে, requestLayout() পূর্ণ onMeasure → onLayout → onDraw চক্র ট্রিগার করে।
  • কাস্টম Views UI-কে 15–40% গতি বাড়ায়, কিন্তু সঠিক onMeasure বাস্তবায়ন এবং onDraw-এ অবজেক্ট ক্যাশিং প্রয়োজন।
  • জটিল গ্রাফিক্সের জন্য, হার্ডওয়্যার ত্বরণ ব্যবহার করুন এবং Path/Bitmap ক্যাশ করুন।

আমরা একটি মোবাইল অ্যাপ্লিকেশন টার্নকি তৈরি করব

IT Sectr 2017 সাল থেকে স্টার্টআপ এবং ব্যবসার জন্য iOS এবং Android অ্যাপ্লিকেশন তৈরি করে। আমরা আপনাকে পরামর্শ দেব এবং সেরা সমাধান প্রস্তাব করব।

প্রকল্প নিয়ে আলোচনা করুন

আরও পড়ুন