onLayout() হল ViewGroup ক্লাসের একটি মেথড যা প্যারেন্ট কন্টেইনারের কোঅর্ডিনেট প্লেনে চাইল্ড View-এর অবস্থান এবং আকার নির্ধারণ করে। Android সিস্টেম মাপার ধাপ (onMeasure) শেষে onLayout কল করে, যখন প্রতিটি চাইল্ড View-এর জন্য মাপা প্রস্থ এবং উচ্চতা ইতিমধ্যে জানা যায়। Android Developers Documentation (2026) অনুসারে, যেকোনো কাস্টম ViewGroup-এ onLayout ওভাররাইড করা বাধ্যতামূলক, কারণ স্ট্যান্ডার্ড ViewGroup বাস্তবায়ন স্বয়ংক্রিয়ভাবে চাইল্ড অবস্থান নির্ধারণ করে না।
মূল পয়েন্ট
onLayout(boolean changed, int l, int t, int r, int b) হল ViewGroup ক্লাসের একটি protected মেথড যা সিস্টেম প্যারেন্ট কন্টেইনারের ভিতরে চাইল্ড View-এর অবস্থান নির্ধারণের জন্য কল করে। ডেভেলপার এই মেথডটি ওভাররাইড করে যখন তিনি নন-স্ট্যান্ডার্ড এলিমেন্ট বিন্যাস সহ কাস্টম ViewGroup তৈরি করেন: ক্যাসকেড, গ্রিড, দাবাবোর্ড প্যাটার্ন বা ইচ্ছামত কোঅর্ডিনেটে। প্রতিটি চাইল্ড View child.layout() কলের মাধ্যমে তার চূড়ান্ত সীমানা পায়।
changed প্যারামিটার ইঙ্গিত দেয় যে শেষ layout-এর পর থেকে ViewGroup-এর অবস্থান বা আকার পরিবর্তিত হয়েছে কিনা। যদি changed সত্য হয়, সমস্ত চাইল্ড এলিমেন্টের সম্ভবত পুনরায় অবস্থান নির্ধারণের প্রয়োজন। প্যারামিটার l, t, r, b হল তার প্যারেন্টের কোঅর্ডিনেট সিস্টেমে ViewGroup-এর উপরের-বাম এবং নিচের-ডান কোণের কোঅর্ডিনেট। onLayout-এর ভিতরে, ডেভেলপার চাইল্ড বিন্যাসের জন্য এই মানগুলি প্রারম্ভিক কোঅর্ডিনেট হিসাবে ব্যবহার করে।
ViewGroup হল একমাত্র ক্লাস যা onLayout ওভাররাইড করে। একটি সাধারণ View (ViewGroup নয়)-এর চাইল্ড এলিমেন্ট থাকে না এবং এটির onLayout-এর প্রয়োজন হয় না — এর অবস্থান নির্ধারণ প্যারেন্ট কন্টেইনার দ্বারা পরিচালিত হয়। এমনকি যদি একটি সাধারণ View onLayout ওভাররাইড করে, সিস্টেম এটি কল করবে না। এটি onMeasure থেকে একটি মৌলিক পার্থক্য, যা যেকোনো View-এর জন্য কল করা হয়।
লেআউট ধাপ রুট View-এ পাবলিক মেথড layout(int l, int t, int r, int b) কলের মাধ্যমে শুরু হয়। এই মেথডটি View-এর চূড়ান্ত কোঅর্ডিনেট সেট করে এবং View ViewGroup হলে onLayout কল করে। তারপর onLayout প্রতিটি চাইল্ড এলিমেন্টের জন্য পুনরাবৃত্তভাবে child.layout() কল করে, এবং প্রক্রিয়াটি শ্রেণিবিন্যাসে নিচের দিকে পুনরাবৃত্ত হয়। এইভাবে, layout মূল থেকে পাতায় ছড়িয়ে পড়ে।
onLayout কল করার আগে, সিস্টেম পরীক্ষা করে যে আগের চক্রের তুলনায় View-এর মাত্রা পরিবর্তিত হয়েছে কিনা। যদি মাত্রা পরিবর্তিত না হয় এবং requestLayout কল না করা হয়, তাহলে onLayout কল নাও হতে পারে — সিস্টেম আগের layout-এর ফলাফল ব্যবহার করে। এটি একটি অপ্টিমাইজেশন যা অ্যানিমেশন বা স্ক্রলিংয়ের সময় অপ্রয়োজনীয় অবস্থান পুনর্গণনা প্রতিরোধ করে, যখন শুধুমাত্র বিষয়বস্তু পরিবর্তিত হয় কিন্তু মাত্রা নয়।
requestLayout() একটি View মেথড যা সিস্টেমকে জানায় যে View-এর layout পুরানো হয়ে গেছে এবং পুনর্গণনা প্রয়োজন। requestLayout কল করলে সম্পূর্ণ চক্র শুরু হয়: প্রথমে onMeasure কল হয়, তারপর onLayout, তারপর onDraw। invalidate-এর বিপরীতে, যা শুধুমাত্র পুনরায় অঙ্কন ট্রিগার করে, requestLayout মাত্রা এবং অবস্থানের সম্পূর্ণ পুনর্গণনা ট্রিগার করে। অত্যধিক কল requestLayout-এ কর্মক্ষমতা সমস্যার একটি সাধারণ কারণ।
l (left) — তার প্যারেন্টের কোঅর্ডিনেট সিস্টেমে ViewGroup-এর বাম প্রান্তের X-কোঅর্ডিনেট। t (top) — উপরের প্রান্তের Y-কোঅর্ডিনেট। r (right) — ডান প্রান্তের X-কোঅর্ডিনেট। b (bottom) — নিচের প্রান্তের Y-কোঅর্ডিনেট। ViewGroup-এর প্রস্থ r - l হিসাবে গণনা করা হয়, উচ্চতা b - t হিসাবে। এই কোঅর্ডিনেটগুলি ইতিমধ্যে ViewGroup-এর সমস্ত প্যাডিং অন্তর্ভুক্ত করে।
onLayout-এর ভিতরে, ডেভেলপার প্রতিটি চাইল্ড View-এর জন্য child.layout(int childLeft, int childTop, int childRight, int childBottom) কল করে। child.layout-এ পাস করা কোঅর্ডিনেটগুলি প্যারেন্ট ViewGroup-এর কোঅর্ডিনেট সিস্টেমে হতে হবে। সাধারণত childLeft এবং childTop প্যারেন্টের প্যাডিং বিবেচনা করে গণনা করা হয়: childLeft = l + paddingLeft + offsetX, childTop = t + paddingTop + offsetY।
| প্যারামিটার | বিবরণ | সাধারণ ব্যবহার |
|---|---|---|
| l (left) | প্যারেন্টে ViewGroup-এর বাম প্রান্তের কোঅর্ডিনেট | চাইল্ড এলিমেন্টের জন্য X-অক্ষে প্রারম্ভিক বিন্দু |
| t (top) | প্যারেন্টে ViewGroup-এর উপরের প্রান্তের কোঅর্ডিনেট | চাইল্ড এলিমেন্টের জন্য Y-অক্ষে প্রারম্ভিক বিন্দু |
| r (right) | প্যারেন্টে ViewGroup-এর ডান প্রান্তের কোঅর্ডিনেট | প্রস্থের উপরের সীমা, r - l = getWidth() |
| b (bottom) | প্যারেন্টে ViewGroup-এর নিচের প্রান্তের কোঅর্ডিনেট | উচ্চতার উপরের সীমা, b - t = getHeight() |
চাইল্ড কোঅর্ডিনেট সূত্র দ্বারা গণনা করা হয়: childLeft = l + paddingLeft + (marginLeft থাকলে), childRight = childLeft + child.getMeasuredWidth()। একইভাবে উল্লম্ব অক্ষের জন্য: childTop = t + paddingTop + (marginTop), childBottom = childTop + child.getMeasuredHeight()। এই চারটি মান গণনা করার পর, child.layout(childLeft, childTop, childRight, childBottom) কল করা হয়।
আসুন একটি FlowLayout তৈরি করি — একটি কাস্টম ViewGroup যা চাইল্ড View-গুলিকে সারিতে সাজায়, বর্তমান সারি পূর্ণ হলে উপাদানগুলিকে নতুন সারিতে স্থানান্তর করে। এটি একটি সমতলে wrap সহ Flexbox-এর সমতুল্য। onLayout সমস্ত চাইল্ড View-এর মাধ্যমে পুনরাবৃত্তি করে, প্রতিটির জন্য অবস্থান গণনা করে এবং সঠিক সীমানা সহ child.layout() কল করে।
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 এবং onLayout View জীবনচক্রের দুটি ক্রমিক ধাপ যা মৌলিকভাবে ভিন্ন কাজ করে। onMeasure একটি View-এর কাঙ্ক্ষিত (মাপা) মাত্রা নির্ধারণ করে, যখন onLayout প্রকৃত (চূড়ান্ত) কোঅর্ডিনেট এবং মাত্রা সেট করে। মূল পার্থক্য: onMeasure-এ, মাত্রাগুলি অন্তর্বর্তী হতে পারে এবং পরে প্যারেন্ট দ্বারা সামঞ্জস্য করা যেতে পারে, যেখানে onLayout-এ প্রতিটি চাইল্ড View-এর চূড়ান্ত অবস্থান স্থির করা হয়।
onMeasure প্রতিটি View-এর জন্য কল করা হয়, লিফ Views (TextView, ImageView, Button) সহ। onLayout শুধুমাত্র ViewGroup-এর জন্য কল করা হয়। এর কারণ হল অবস্থান নির্ধারণ প্যারেন্ট কন্টেইনারের দায়িত্ব, View-এর নিজের নয়। একটি লিফ View প্যারেন্টের onLayout থেকে কল করা layout()-এর মাধ্যমে তার অবস্থান পায়।
getMeasuredWidth() এবং getMeasuredHeight() onMeasure-এর পরে উপলব্ধ, যেখানে getWidth() এবং getHeight() শুধুমাত্র onLayout-এর পরে উপলব্ধ। আপনি যদি onMeasure-এর ভিতরে getWidth() অ্যাক্সেস করেন, এটি আগের চক্রের মান বা শূন্য ফিরিয়ে দেবে। তাই, onMeasure-এ মাত্রা গণনার জন্য, আপনার ক্রমানুসারে MeasureSpec এবং চাইল্ড ব্যবহার করা উচিত।
প্যাডিং বিবেচনা না করে অবস্থান নির্ধারণ — onLayout বাস্তবায়নের সময় প্রথম ভুল। ডেভেলপার প্রায়ই চাইল্ড View-এর প্রারম্ভিক কোঅর্ডিনেটে প্যারেন্টের paddingLeft এবং paddingTop যোগ করতে ভুলে যান। ফলস্বরূপ, চাইল্ড ViewGroup-এর প্রান্তে প্রদর্শিত হয়, setPadding() বা XML মার্কআপের মাধ্যমে সেট করা প্যাডিং উপেক্ষা করে। সঠিক গণনা: childLeft = paddingLeft + offsetX।
অদৃশ্য চাইল্ডের জন্য layout কল করা — দ্বিতীয় সাধারণ সমস্যা। যদি ViewGroup-এ GONE দৃশ্যমানতা সহ চাইল্ড Views থাকে, তাহলে তাদের অবস্থান নির্ধারণের প্রয়োজন নেই — তারা কোনও স্থান নেয় না। তবে, onLayout-কে এই ক্ষেত্রটি সঠিকভাবে পরিচালনা করতে হবে, GONE চাইল্ড বাদ দিয়ে। INVISIBLE চাইল্ডের জন্য, এখনও layout কল করা প্রয়োজন — তারা প্রদর্শিত না হলেও তাদের স্থান ধরে রাখে।
changed প্যারামিটার উপেক্ষা করা — তৃতীয় ভুল। changed প্যারামিটার ইঙ্গিত দেয় যে ViewGroup-এর মাত্রা বা অবস্থান পরিবর্তিত হয়েছে কিনা। যদি changed == false হয়, তাহলে সমস্ত চাইল্ড এলিমেন্টের layout পুনর্গণনা না করেই ক্যাশেড কোঅর্ডিনেট ব্যবহার করা যেতে পারে। তবে, সম্পূর্ণ layout ক্যাশিং একটি জটিল কাজ, এবং বেশিরভাগ বাস্তবায়নে onLayout প্রতিবার কেবল সমস্ত এলিমেন্ট পুনর্গণনা করে। এটি অল্প সংখ্যক চাইল্ডের সাথে গ্রহণযোগ্য।
সচরাচর জিজ্ঞাসিত প্রশ্ন
হ্যাঁ, সম্ভব যদি ViewGroup স্ট্যান্ডার্ড LayoutParams ব্যবহার করে এবং কাস্টম অবস্থান নির্ধারণ লজিক যোগ না করে। তবে, ViewGroup-এ onLayout-এর স্ট্যান্ডার্ড বাস্তবায়ন কোনও কর্ম সম্পাদন করে না — চাইল্ড এলিমেন্টগুলির অবস্থান নির্ধারিত হবে না। বাস্তবে, সমস্ত ViewGroup (LinearLayout, RelativeLayout, FrameLayout) onLayout ওভাররাইড করে।
layout() হল View-এর একটি পাবলিক ফাইনাল মেথড, যা সিস্টেম বা প্যারেন্ট ViewGroup দ্বারা কল করা হয়। এটি View-এর কোঅর্ডিনেট সেট করে এবং View ViewGroup হলে onLayout কল করে। onLayout() হল একটি protected মেথড যা ডেভেলপার চাইল্ড এলিমেন্টের কাস্টম বিন্যাসের জন্য ওভাররাইড করে।
প্রযুক্তিগতভাবে — হ্যাঁ, পারে। তবে এটি স্পষ্টভাবে সুপারিশ করা হয় না, কারণ এটি অসীম পুনরাবৃত্তির দিকে নিয়ে যায়: requestLayout → onMeasure → onLayout → requestLayout। যদি onLayout-এর ভিতরে requestLayout কল করা হয়, সিস্টেম StackOverflowError ব্যতিক্রম ছুঁড়বে। সমস্ত মাত্রা পরিবর্তন onLayout-এর আগে করা উচিত।
লেআউট অ্যানিমেশন (LayoutTransition) চাইল্ড View-এর অবস্থানে পরিবর্তন আটকায় এবং ট্রানজিশন অ্যানিমেশন প্রয়োগ করে। যখন LayoutTransition সক্ষম হয়, onLayout প্রথমে চূড়ান্ত অবস্থান সেট করে, তারপর LayoutTransition পুরানো অবস্থান থেকে নতুন অবস্থানে গতি অ্যানিমেট করে। এর জন্য সঠিক চূড়ান্ত কোঅর্ডিনেট সহ সঠিক onLayout বাস্তবায়ন প্রয়োজন।
invalidate() শুধুমাত্র ড্র ধাপ (পুনরায় অঙ্কন) ট্রিগার করে, measure এবং layout-কে প্রভাবিত না করে। onLayout ট্রিগার করতে, আপনাকে requestLayout() কল করতে হবে, যা সম্পূর্ণ চক্র শুরু করে: measure → layout → draw। invalidate চেহারা আপডেট করার জন্য বেশি কার্যকর যখন মাত্রা এবং অবস্থান পরিবর্তিত হয় না।
সারসংক্ষেপ
আমরা একটি মোবাইল অ্যাপ্লিকেশন টার্নকি তৈরি করব
IT Sectr 2017 সাল থেকে স্টার্টআপ এবং ব্যবসার জন্য iOS এবং Android অ্যাপ্লিকেশন তৈরি করে। আমরা আপনাকে পরামর্শ দেব এবং সেরা সমাধান প্রস্তাব করব।
আরও পড়ুন