onMeasure() হল android.view.View ক্লাসের একটি protected পদ্ধতি যা Android সিস্টেম একটি View-এর আকার নির্ধারণের জন্য কল করে। সিস্টেম পদ্ধতিতে দুটি MeasureSpec অবজেক্ট পাঠায়, প্রতিটিতে একটি পরিমাপ মোড (EXACTLY, AT_MOST বা UNSPECIFIED) এবং প্যারেন্ট কন্টেইনার দ্বারা প্রস্তাবিত একটি আকার থাকে। Android Developers Documentation (2026) অনুসারে, সঠিক MeasureSpec হ্যান্ডলিং সহ onMeasure ওভাররাইড করা সমস্ত কাস্টম View এবং ViewGroup-এর জন্য বাধ্যতামূলক যার আকারের উপর সুনির্দিষ্ট নিয়ন্ত্রণ প্রয়োজন।
মূল বিষয়
onMeasure(int widthMeasureSpec, int heightMeasureSpec) হল View ক্লাসের একটি পদ্ধতি যা Android সিস্টেম একটি ভিউয়ের প্রস্থ এবং উচ্চতা নির্ধারণের জন্য কল করে। ডেভেলপার এই পদ্ধতিটি ওভাররাইড করে নির্দিষ্ট করে যে MeasureSpec-এ পাঠানো সীমাবদ্ধতার ভিত্তিতে View-এর আকার কত হওয়া উচিত। সঠিক onMeasure ওভাররাইডিং ছাড়া, কাস্টম View ভুলভাবে প্রদর্শিত হতে পারে বা একেবারেই দেখা নাও যেতে পারে।
সিস্টেম View জীবনচক্রের measure ধাপের সময় onMeasure কল করে, যা layout (onLayout) এবং draw (onDraw) ধাপের আগে আসে। যদি কোনো View onMeasure ওভাররাইড না করে, তাহলে সুপারক্লাসের বাস্তবায়ন ব্যবহার করা হয়, যা background drawable বা layout_params-এর ভিত্তিতে ডিফল্ট আকার নির্ধারণ করে। super.onMeasure(widthMeasureSpec, heightMeasureSpec) কল করা শুধুমাত্র View-এর মানক উপশ্রেণী যেমন TextView বা ImageView-এর জন্য কাজ করে।
onMeasure-এর একটি মূল প্রয়োজনীয়তা হল setMeasuredDimension(int, int)-এর কল পদ্ধতির শেষে উপস্থিত থাকতে হবে। যদি এই কলটি অনুপস্থিত থাকে, তাহলে সিস্টেম IllegalStateException নিক্ষেপ করে জানায় যে View পরিমাপকৃত মাত্রা নির্ধারণ করেনি। পরিমাপ ধাপ শেষ হওয়ার পরে চূড়ান্ত মাত্রাগুলি getMeasuredWidth() এবং getMeasuredHeight() গেটারগুলির মাধ্যমে উপলব্ধ হয়।
MeasureSpec হল একটি 32-বিট পূর্ণসংখ্যা যেখানে উপরের 2 বিট পরিমাপ মোড এনকোড করে এবং নীচের 30 বিট আকার এনকোড করে। মোড নির্ধারণ করে যে View তার নিজস্ব আকার নির্বাচনে কতটা স্বাধীন। Android তিনটি মোড সরবরাহ করে: EXACTLY, AT_MOST এবং UNSPECIFIED। প্রতিটি মোড onMeasure-তে ভিন্ন প্রক্রিয়াকরণ যুক্তি নির্দেশ করে।
| MeasureSpec মোড | মান | আচরণ |
|---|---|---|
| EXACTLY | প্যারেন্ট সঠিক আকার নির্দিষ্ট করেছে | View-কে প্রদত্ত আকারে ঠিকভাবে ফিট হতে হবে যদি এটি সীমানার বাইরে যেতে না চায় |
| AT_MOST | প্যারেন্ট সর্বোচ্চ আকার নির্ধারণ করেছে | View 0 থেকে প্রদত্ত সর্বোচ্চ পর্যন্ত যেকোনো আকার নির্বাচন করতে পারে |
| UNSPECIFIED | প্যারেন্ট কোনো সীমাবদ্ধতা আরোপ করে না | View কোনো ঊর্ধ্বসীমা ছাড়াই যেকোনো পছন্দসই আকার নির্বাচন করতে পারে |
MeasureSpec থেকে মোড এবং আকার বের করতে MeasureSpec ক্লাসের স্ট্যাটিক পদ্ধতি ব্যবহার করা হয়: MeasureSpec.getMode(int) তিনটি মোডের (EXACTLY, AT_MOST, UNSPECIFIED) একটি ফেরত দেয়, এবং MeasureSpec.getSize(int) পিক্সেলে সংখ্যাসূচক আকার ফেরত দেয়। কাস্টম MeasureSpec তৈরি করতে MeasureSpec.makeMeasureSpec(int size, int mode) ব্যবহার করা হয়। এই তিনটি পদ্ধতি onMeasure-তে আকার নিয়ে কাজ করার সমস্ত পরিস্থিতি কভার করে।
মানক প্যাটার্ন MeasureSpec হ্যান্ডলিংয়ের জন্য: যদি মোড EXACTLY হয় — প্রদত্ত আকারটি চূড়ান্ত হিসাবে ব্যবহার করুন; যদি AT_MOST হয় — পছন্দসই আকার (View-এর বিষয়বস্তু) এবং প্রদত্ত সর্বোচ্চের মধ্যে ন্যূনতম নির্বাচন করুন; যদি UNSPECIFIED হয় — সীমাবদ্ধতা ছাড়াই View-এর পছন্দসই আকার ব্যবহার করুন। এই প্যাটার্ন প্যারেন্টের যেকোনো সীমাবদ্ধতার অধীনে সঠিক আচরণ নিশ্চিত করে।
override fun onMeasure(widthMeasureSpec: Int,
heightMeasureSpec: Int) {
val desiredWidth = 200
val desiredHeight = 100
val widthMode = MeasureSpec.getMode(widthMeasureSpec)
val widthSize = MeasureSpec.getSize(widthMeasureSpec)
val heightMode = MeasureSpec.getMode(heightMeasureSpec)
val heightSize = MeasureSpec.getSize(heightMeasureSpec)
val width = when (widthMode) {
MeasureSpec.EXACTLY -> widthSize
MeasureSpec.AT_MOST -> minOf(desiredWidth, widthSize)
else -> desiredWidth
}
val height = when (heightMode) {
MeasureSpec.EXACTLY -> heightSize
MeasureSpec.AT_MOST -> minOf(desiredHeight, heightSize)
else -> desiredHeight
}
setMeasuredDimension(width, height)
}
মানক যুক্তি সরল করতে, Android resolveSizeAndState পদ্ধতি সরবরাহ করে যা পছন্দসই আকার এবং MeasureSpec গ্রহণ করে এবং সঠিক মোড সহ চূড়ান্ত আকার ফেরত দেয়। এই পদ্ধতি উপরে বর্ণিত প্যাটার্নটি কোডের এক লাইনে বাস্তবায়ন করে। resolveSize(int size, int measureSpec) ফাংশনও উপলব্ধ, যা স্টেট বিট ছাড়া একটি পরিষ্কার আকার ফেরত দেয়।
Android একটি দ্বি-পাস পরিমাপ অ্যালগরিদম ব্যবহার করে যা নিশ্চিত করে যে স্তরক্রমের প্রতিটি View প্যারেন্ট সীমাবদ্ধতা এবং চাইল্ড পছন্দ বিবেচনা করে সঠিক মাত্রা পায়। প্রথম পাসে, প্যারেন্ট সীমাবদ্ধতা সহ MeasureSpec চাইল্ড View-তে পাঠায়, এবং চাইল্ড View তাদের পছন্দসই আকার গণনা করে। দ্বিতীয় পাসে, প্যারেন্ট আকার সম্পর্কে চূড়ান্ত সিদ্ধান্ত নেয়।
ViewGroup-এর জন্য, পরিমাপ প্রক্রিয়া আরও জটিল: প্যারেন্টকে প্রথমে তার সমস্ত সন্তানকে পরিমাপ করতে হবে, তারপর তাদের আকারের ভিত্তিতে নিজের আকার নির্ধারণ করতে হবে। measureChildren(int widthMeasureSpec, int heightMeasureSpec) কল করা সমস্ত চাইল্ড View-এর মাধ্যমে পুনরাবৃত্তি করে এবং প্রতিটির জন্য measure(child, childWidthSpec, childHeightSpec) কল করে। সমস্ত সন্তানকে পরিমাপ করার পরে, ViewGroup তার নিজস্ব মাত্রা সহ setMeasuredDimension কল করে।
একটি গুরুত্বপূর্ণ সূক্ষ্মতা: measure পদ্ধতি (পাবলিক, ফাইনাল) ওভাররাইড করা যায় না — পরিবর্তে onMeasure ওভাররাইড করা হয়। এটি নিশ্চিত করে যে সিস্টেম onMeasure-এর আগে এবং পরে রক্ষণাবেক্ষণ কাজ সম্পাদন করতে পারে, যেমন আকার পরিবর্তন পরীক্ষা করা এবং পরবর্তী অঙ্কনের জন্য নোংরা এলাকা গণনা করা। যদি কোনো View-এর নির্দিষ্ট মাত্রা থাকে, তাহলে onMeasure ওভাররাইড করার প্রয়োজন নাও হতে পারে।
MeasureSpec শুধুমাত্র আকার এবং মোড নয় বরং স্টেট বিটও অন্তর্ভুক্ত করে, যা MeasureSpec.getMode()-এর মাধ্যমে অ্যাক্সেসযোগ্য। setMeasuredDimension কল করার পরে, অবস্থাটি View-এর পরিমাপকৃত মাত্রার অংশ হয়ে যায় এবং getMeasuredState()-এর মাধ্যমে পরীক্ষা করা যায়। এটি ScrollView এবং অন্যান্য স্ক্রলযোগ্য কন্টেইনারে সন্তানদের কাছে সীমাবদ্ধতা সঠিকভাবে পাঠাতে ব্যবহৃত হয়।
আসুন বর্গাকার প্রদর্শনের জন্য onMeasure সহ একটি কাস্টম View তৈরির ব্যবহারিক উদাহরণ দেখি। SquareView ক্লাস View-কে প্রসারিত করে এবং নিশ্চিত করে যে প্রস্থ এবং উচ্চতা সর্বদা সমান, পাঠানো MeasureSpec যাই হোক না কেন। onMeasure-এ, ন্যূনতম বাহু নির্ধারণ করা হয় এবং বর্গাকার আকার সেট করা হয়।
class SquareView(context: Context)
: View(context) {
override fun onMeasure(widthMeasureSpec: Int,
heightMeasureSpec: Int) {
val widthSize =
MeasureSpec.getSize(widthMeasureSpec)
val heightSize =
MeasureSpec.getSize(heightMeasureSpec)
val size = minOf(widthSize, heightSize)
setMeasuredDimension(size, size)
}
}
ViewGroup-এর আরও জটিল onMeasure যুক্তি প্রয়োজন কারণ প্রথমে সন্তানদের পরিমাপ করতে হবে, তারপর ViewGroup-এর নিজস্ব আকার নির্ধারণ করা হয়। CascadeLayout উদাহরণ সন্তানদের একটি অফসেট সহ ক্যাসকেডে বিতরণ করে। measureChildWithMargins-এর মাধ্যমে সমস্ত সন্তানকে পরিমাপ করার পরে, মোট প্রস্থ এবং উচ্চতা গণনা করা হয়।
class CascadeLayout(context: Context)
: ViewGroup(context) {
private val cascadeOffset = 40
override fun onMeasure(widthMeasureSpec: Int,
heightMeasureSpec: Int) {
var maxWidth = 0
var totalHeight = 0
for (i in 0 until childCount) {
val child = getChildAt(i)
measureChildWithMargins(child,
widthMeasureSpec,
cascadeOffset * i,
heightMeasureSpec, 0)
maxWidth = maxOf(maxWidth,
child.measuredWidth +
cascadeOffset * i)
totalHeight += child.measuredHeight
}
setMeasuredDimension(
resolveSize(maxWidth, widthMeasureSpec),
resolveSize(totalHeight, heightMeasureSpec))
}
override fun generateLayoutParams(attrs: AttributeSet?)
: LayoutParams = MarginLayoutParams(context, attrs)
override fun onLayout(changed: Boolean,
l: Int, t: Int,
r: Int, b: Int) {
var top = t
for (i in 0 until childCount) {
val child = getChildAt(i)
val left = l + cascadeOffset * i
child.layout(left, top,
left + child.measuredWidth,
top + child.measuredHeight)
top += child.measuredHeight
}
}
}
setMeasuredDimension কল মিস করা সবচেয়ে সাধারণ ভুল। যদি ডেভেলপার onMeasure ওভাররাইড করে কিন্তু setMeasuredDimension কল না করে, তাহলে অ্যাপ্লিকেশন IllegalStateException-সহ ক্র্যাশ করে। এটি বিশেষ করে তখন ঘটে যখন পদ্ধতিতে শর্তসাপেক্ষ শাখা থাকে এবং একটি শাখায় কল অনুপস্থিত থাকে। onMeasure-এ কোডের প্রতিটি শাখা setMeasuredDimension কল দিয়ে শেষ হওয়া উচিত।
AT_MOST মোড উপেক্ষা করা দ্বিতীয় সবচেয়ে সাধারণ ভুল। যদি একটি View AT_MOST মোডে বিষয়বস্তুর ভিত্তিতে গণনা করার পরিবর্তে সর্বদা পাঠানো আকার ব্যবহার করে, তাহলে প্যারেন্ট কন্টেইনার সঠিকভাবে স্থান বিতরণ করতে পারে না। উদাহরণস্বরূপ, AT_MOST-এ TextView-এর টেক্সটের প্রস্থ গণনা করা উচিত এবং পছন্দসই এবং পাঠানো প্রস্থের মধ্যে ন্যূনতম ব্যবহার করা উচিত। AT_MOST উপেক্ষা করলে View সামান্য বিষয়বস্তু থাকলেও সমস্ত উপলব্ধ স্থান দখল করে।
onMeasure-এর ভিতরে অবজেক্ট তৈরি করা একটি ক্লাসিক কর্মক্ষমতা ভুল। যেহেতু onMeasure একাধিকবার কল করা যেতে পারে (প্রত্যেক layout অনুরোধে), এই পদ্ধতির ভিতরে অবজেক্ট (Paint, Rect, String) তৈরি করা মেমরি জটিল করে এবং আবর্জনা সংগ্রহ সক্রিয় করে। সমস্ত অবজেক্ট View কনস্ট্রাক্টরে একবার তৈরি করা উচিত, এবং onMeasure-এ শুধুমাত্র আকার গণনার যুক্তি চলা উচিত। এই একই নিয়ম onDraw এবং onLayout-এর ক্ষেত্রেও প্রযোজ্য।
measureChildWithMargins হল ViewGroup-এর একটি সুরক্ষিত পদ্ধতি যা একটি একক চাইল্ড View-কে তার MarginLayoutParams বিবেচনা করে পরিমাপ করে। পদ্ধতিটি প্যারেন্টের MeasureSpec এবং সঞ্চিত প্রস্থ ও উচ্চতা অফসেট গ্রহণ করে। এটি প্যারেন্টের প্যাডিং এবং চাইল্ডের মার্জিন বিয়োগ করে চাইল্ড View-এর জন্য MeasureSpec স্বয়ংক্রিয়ভাবে সামঞ্জস্য করে, তারপর সামঞ্জস্যকৃত MeasureSpec child.measure()-এ পাঠায়।
উন্নত পরিমাপ যুক্তি-র জন্য, ViewGroup measureChild(View child, int parentWidthSpec, int parentHeightSpec) ওভাররাইড করতে পারে বা প্রতিটি সন্তানের জন্য সরাসরি MeasureSpec-এর সাথে কাজ করতে পারে। উদাহরণস্বরূপ, LinearLayout onMeasure-এ সমস্ত চাইল্ড View-এর মাধ্যমে পুনরাবৃত্তি করে, প্রতিটিকে তার layout_weight বিবেচনা করে পরিমাপ করে এবং অবশিষ্ট স্থান আনুপাতিকভাবে বিতরণ করে। এই পদ্ধতি নির্বিচারে লেআউট অ্যালগরিদম বাস্তবায়নের অনুমতি দেয়।
মাপ ক্যাশ প্রক্রিয়ার মাধ্যমে পরিমাপ ফলাফল ক্যাশ করা নির্দিষ্ট ViewGroup-এ setMeasureWithLargestChildEnabled ফ্ল্যাগের মাধ্যমে উপলব্ধ। তবে, বেশিরভাগ ক্ষেত্রে onMeasure যেকোনো layout পরিবর্তনে পুনরায় কল করা হয় এবং ক্যাশিং প্রযোজ্য নয়। কাস্টম ViewGroup-এ, ক্যাশিংয়ের উপর নির্ভর না করে onMeasure-এ গণনা কমানোর পরামর্শ দেওয়া হয়।
সচরাচর জিজ্ঞাসিত প্রশ্ন
হ্যাঁ, যদি কাস্টম View সরাসরি View ক্লাস থেকে ইনহেরিট করে। যদি এটি TextView, ImageView বা Button থেকে তাদের মানক আকার সহ ইনহেরিট করে, তাহলে onMeasure অপরিবর্তিত রাখা যেতে পারে। ViewGroup-এর জন্য, onMeasure ওভাররাইড করা সর্বদা প্রয়োজন — অন্যথায় সন্তানরা সঠিকভাবে পরিমাপ করা হবে না।
Android সিস্টেম IllegalStateException নিক্ষেপ করে যার বার্তা “The View did not call setMeasuredDimension”। এই ব্যতিক্রমটি measure() পদ্ধতিতে onMeasure শেষ হওয়ার পরে ঘটে, যদি চূড়ান্ত মাত্রা শূন্য থাকে। try-catch-এর মাধ্যমে না সামলালে ব্যতিক্রমটি অ্যাপ্লিকেশন ক্র্যাশ করে।
getMeasuredWidth() onMeasure-এ (পরিমাপ ধাপ) নির্ধারিত আকার ফেরত দেয়। getWidth() প্রকৃত আকার ফেরত দেয় যা View onLayout-এ সমস্ত অবস্থান সমন্বয়ের পরে পেয়েছে। বেশিরভাগ View-এর জন্য এই মানগুলি মিলে যায়, কিন্তু কাস্টম ViewGroup-এ এগুলি ভিন্ন হতে পারে।
না, onMeasure শুধুমাত্র আকার গণনার উদ্দেশ্যে। এই পদ্ধতিতে অবস্থা পরিবর্তন করা, অ্যানিমেশন শুরু করা, নেটওয়ার্কিং বা ডেটা আপডেট করা Android আর্কিটেকচার লঙ্ঘন করে এবং পুনরাবৃত্তিমূলক measure কলের কারণ হতে পারে, কারণ অবস্থা পরিবর্তন requestLayout ট্রিগার করতে পারে।
ConstraintLayout নির্ধারিত সীমাবদ্ধতার ভিত্তিতে স্বাধীনভাবে সন্তানদের পরিমাপ পরিচালনা করে। যদি ConstraintLayout-এর ভিতরে একটি কাস্টম View onMeasure ওভাররাইড করে, তাহলে তাকে ConstraintLayout থেকে পাঠানো MeasureSpec সঠিকভাবে সামলাতে হবে, অন্যথায় সীমাবদ্ধতা কাজ নাও করতে পারে। ConstraintLayout গণনার জন্য নিজস্ব WidgetContainer-সহ একটি দ্বি-পাস অ্যালগরিদম ব্যবহার করে।
সারসংক্ষেপ
আমরা একটি মোবাইল অ্যাপ্লিকেশন টার্নকি তৈরি করব
IT Sectr 2017 সাল থেকে স্টার্টআপ এবং ব্যবসার জন্য iOS এবং Android অ্যাপ্লিকেশন তৈরি করে। আমরা আপনাকে পরামর্শ দেব এবং সেরা সমাধান প্রস্তাব করব।
আরও পড়ুন