Custom View হল Android-এ একটি কাস্টম ইন্টারফেস উপাদান, যা View ক্লাস বা এর উপশ্রেণীগুলি (Button, TextView, ImageView) থেকে ইনহেরিট করে এবং মূল মেথডগুলি ওভাররাইড করে তৈরি করা হয়: অঙ্কনের জন্য onDraw, আকার মাপার জন্য onMeasure এবং টাচ হ্যান্ডলিং-এর জন্য onTouchEvent। Google Android Developer Guide (2024) অনুসারে, Custom View ব্যবহার করা হয় যখন স্ট্যান্ডার্ড Android SDK উপাদানগুলি প্রয়োজনীয় আচরণ বা চেহারা প্রদান করে না — যেমন কাস্টম অ্যানিমেশন, অ-মানক আকার, বিশেষায়িত চার্ট এবং গেম উপাদানের জন্য।
মুখ্য বিষয়
Custom View হল একটি ক্লাস যা android.view.View (বা এর একটি উপশ্রেণী) থেকে ইনহেরিট করে এবং কাস্টম অঙ্কন, মাপ ও ইভেন্ট হ্যান্ডলিং লজিক বাস্তবায়নের জন্য সিস্টেম মেথডগুলি ওভাররাইড করে। Custom View অনন্য UI উপাদান তৈরি করার জন্য Android-এর একটি মৌলিক প্রক্রিয়া।
Android Custom View তৈরি করার দুটি পদ্ধতি প্রদান করে: View থেকে ইনহেরিট করা (onDraw-এর মাধ্যমে সম্পূর্ণ কাস্টম অঙ্কন) এবং বিদ্যমান View উপশ্রেণী থেকে ইনহেরিট করা (উদাহরণস্বরূপ, বেস আচরণ বজায় রেখে কার্যকারিতা যোগ করার জন্য Button বা TextView ওভাররাইড করা)।
বেসিক কনস্ট্রাক্টর-এ Context এবং AttributeSet গ্রহণ করতে হবে — এটি সিস্টেমকে XML মার্কআপ থেকে View তৈরি করতে দেয়। যদি View শুধুমাত্র কোড থেকে ব্যবহার করা হয়, তাহলে Context সহ একটি কনস্ট্রাক্টর যথেষ্ট। স্টাইল সহ তৃতীয় কনস্ট্রাক্টর Android থিম সমর্থনের জন্য প্রয়োজনীয়।
class CustomView
@JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
private val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
color = Color.BLUE
strokeWidth = 4f
style = Paint.Style.FILL
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
canvas.drawCircle(width / 2f,
height / 2f,
50f, paint)
}
}
ন্যূনতম Custom View-এ একটি কনস্ট্রাক্টর এবং onDraw থাকে। তবে, Android Layout সিস্টেমে সঠিকভাবে কাজ করার জন্য, onMeasure-ও ওভাররাইড করতে হবে — অন্যথায় View শূন্য আকারে প্রদর্শিত হতে পারে বা wrap_content লেআউট প্যারামিটারে ভুল প্রতিক্রিয়া দেখাতে পারে।
Google I/O 2023 অনুসারে, Google Play-এর শীর্ষ 100 অ্যাপের 65%-এ কাস্টম View ব্যবহার করা হয়। Custom View তৈরির সবচেয়ে সাধারণ কারণ: কাস্টম অ্যানিমেশন (35%), কাস্টম চার্ট এবং ডায়াগ্রাম (25%), বিশেষায়িত নিয়ন্ত্রণ উপাদান (20%) এবং ব্র্যান্ডেড উপাদান (20%)।
onMeasure হল সেই মেথড যেখানে View প্রাপ্ত MeasureSpec (প্যারেন্ট সীমাবদ্ধতা) এর ভিত্তিতে তার কাঙ্ক্ষিত মাত্রা জানায়। সঠিক onMeasure বাস্তবায়ন ছাড়া, View-এর wrap_content-এর সাথে শূন্য উচ্চতা থাকতে পারে বা match_parent-এর সাথে পুরো স্ক্রিন এলাকা দখল করতে পারে।
MeasureSpec-এ একটি মোড (EXACTLY, AT_MOST, UNSPECIFIED) এবং একটি মান থাকে। EXACTLY — প্যারেন্ট সঠিক আকার সেট করেছে (match_parent বা নির্দিষ্ট মান)। AT_MOST — প্যারেন্ট সর্বোচ্চ সেট করেছে (wrap_content)। UNSPECIFIED — কোনো সীমাবদ্ধতা নেই (ScrollView, ListView)।
override fun onMeasure(
widthMeasureSpec: Int,
heightMeasureSpec: Int
) {
val desiredWidth = paddingLeft + paddingRight
+ DEFAULT_WIDTH
val desiredHeight = paddingTop + paddingBottom
+ DEFAULT_HEIGHT
val measuredWidth = MeasureSpec.getSize(widthMeasureSpec)
val modeWidth = MeasureSpec.getMode(widthMeasureSpec)
val resultWidth = when (modeWidth) {
MeasureSpec.EXACTLY -> measuredWidth
MeasureSpec.AT_MOST ->
desiredWidth.coerceAtMost(measuredWidth)
else -> desiredWidth
}
val resultHeight = resolveSize(desiredHeight,
heightMeasureSpec)
setMeasuredDimension(resultWidth, resultHeight)
}
resolveSize একটি Android ইউটিলিটি মেথড যা onMeasure বাস্তবায়ন সহজ করে। এটি কাঙ্ক্ষিত আকার এবং MeasureSpec নেয় এবং সঠিক মান ফেরত দেয়: EXACTLY-এর জন্য — সঠিক আকার, AT_MOST-এর জন্য — কাঙ্ক্ষিত এবং সর্বোচ্চের মধ্যে সর্বনিম্ন, UNSPECIFIED-এর জন্য — কাঙ্ক্ষিত আকার।
onLayout View (ViewGroup নয়) এর জন্য সাধারণত ওভাররাইড করা হয় না — এটি প্যারেন্ট দ্বারা চাইল্ড উপাদানগুলি অবস্থানের জন্য কল করা হয়। ViewGroup-এর জন্য, onLayout বাধ্যতামূলক — প্রতিটি চাইল্ড View-এর জন্য layout() কল করতে হবে।
onDraw হল Custom View-এর হৃদয়। এই মেথডটি Android দ্বারা প্রতিটি View পুনরাঙ্কনে কল করা হয়। এটিতে একটি Canvas অবজেক্ট পাঠানো হয়, যার মাধ্যমে সমস্ত অঙ্কন করা হয়: জ্যামিতিক আকার, লেখা, পথ, Bitmap এবং অ্যানিমেশন।
Canvas API সমস্ত মৌলিক অপারেশনের জন্য মেথড প্রদান করে: drawCircle, drawRect, drawLine, drawPath, drawText, drawBitmap, drawArc, drawOval। স্টাইল কনফিগার করার জন্য, Paint ব্যবহার করা হয় — একটি অবজেক্ট যা রং, পুরুত্ব, ভরাট শৈলী, ফন্ট, ছায়া এবং প্রভাব নির্ধারণ করে।
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
// পটভূমি
canvas.drawRect(0f, 0f,
width.toFloat(), height.toFloat(), bgPaint)
// লেখা
canvas.drawText("Custom View",
paddingLeft.toFloat(),
paddingTop.toFloat() + textPaint.textSize,
textPaint)
// বৃত্ত
canvas.drawCircle(
width / 2f,
height / 2f,
radius, circlePaint)
// পথ (বেজিয়ার বক্ররেখা)
val path = Path().apply {
moveTo(0f, height.toFloat())
quadTo(width / 2f, 0f,
width.toFloat(), height.toFloat())
}
canvas.drawPath(path, pathPaint)
}
Paint হল অঙ্কন শৈলী কনফিগারেটর। অ্যান্টি-আলিয়াসিং (Paint.ANTI_ALIAS_FLAG) মসৃণ প্রান্তের জন্য বাধ্যতামূলক। সাবপিক্সেল টেক্সট (SUBPIXEL_TEXT_FLAG) লেখার গুণমান উন্নত করে। Style.FILL, STROKE এবং FILL_AND_STROKE নির্ধারণ করে যে আকারটি ভরা হবে, রূপরেখা দেওয়া হবে বা উভয়ই।
গুরুত্বপূর্ণ: onDraw-এ Paint, Path, Rect অবজেক্ট তৈরি করবেন না — এটি অঙ্কন লুপে বরাদ্দ সৃষ্টি করে এবং আবর্জনা সংগ্রহ শুরু করে, যা FPS কমিয়ে দেয়। সমস্ত অঙ্কন অবজেক্ট কনস্ট্রাক্টর বা init মেথডে তৈরি করুন। অ্যানিমেশনের জন্য, পুনরাঙ্কন শুরু করতে ValueAnimator বা ObjectAnimator ব্যবহার করুন invalidate()-এর সাথে।
কাস্টম অ্যাট্রিবিউট স্ট্যান্ডার্ড android:layout_width বা android:background-এর মতো XML মার্কআপ থেকে Custom View কনফিগার করার অনুমতি দেয়। অ্যাট্রিবিউটগুলি res/values/attrs.xml ফাইলে নাম, প্রকার এবং ঐচ্ছিক ডিফল্ট মান সহ ঘোষণা করা হয়।
অ্যাট্রিবিউটের প্রকার: string, integer, float, boolean, color, dimension, enum, flag, fraction, reference (রিসোর্স রেফারেন্স)। প্রতিটি প্রকারের জন্য, Android স্বয়ংক্রিয়ভাবে XML থেকে মান পার্স করে এবং TypedArray-তে পাঠায়।
<!-- res/values/attrs.xml -->
<resources>
<declare-styleable name="CustomView">
<attr name="circleColor"
format="color" />
<attr name="circleRadius"
format="dimension" />
<attr name="labelText"
format="string" />
<attr name="showAnimation"
format="বুলিয়ান" />
</declare-styleable>
</resources>
Custom View কনস্ট্রাক্টরে, অ্যাট্রিবিউটগুলি context.obtainStyledAttributes-এর মাধ্যমে পড়া হয়, যা TypedArray ফেরত দেয়। TypedArray টাইপকৃত অ্যাক্সেস মেথড প্রদান করে: getColor, getDimension, getString, getBoolean, getInt। পড়ার পর, রিসোর্স মুক্ত করতে TypedArray-তে recycle() কল করতে হবে।
init {
val typedArray = context.obtainStyledAttributes(
attrs,
R.styleable.CustomView
)
circleColor = typedArray.getColor(
R.styleable.CustomView_circleColor,
Color.BLUE
)
circleRadius = typedArray.getDimension(
R.styleable.CustomView_circleRadius,
50f
)
labelText = typedArray.getString(
R.styleable.CustomView_labelText
) ?: "Default"
typedArray.recycle()
}
XML-এ ব্যবহার: app নামস্থান যোগ করুন (xmlns:app="http://schemas.android.com/apk/res-auto") এবং app:circleColor="@color/red"-এর মতো কাস্টম অ্যাট্রিবিউট ব্যবহার করুন। attrs.xml-এ ঘোষণা সঠিক হলে Android Studio অটোকমপ্লিট করবে এবং অ্যাট্রিবিউট প্রকার যাচাই করবে।
onTouchEvent হল সেই মেথড যা View-এর প্রতিটি স্পর্শে কল করা হয়। এটি ইভেন্টের প্রকার (ACTION_DOWN, ACTION_MOVE, ACTION_UP, ACTION_CANCEL), স্থানাঙ্ক, চাপ এবং আঙুলের সংখ্যা (মাল্টি-টাচ) সম্পর্কিত তথ্য সহ একটি MotionEvent অবজেক্ট গ্রহণ করে।
জটিল জেসচার (সোয়াইপ, পিঞ্চ, লং প্রেস) হ্যান্ডল করার জন্য, onTouchEvent-এর সাথে GestureDetector বা ScaleGestureDetector ব্যবহার করুন। GestureDetector onSingleTapUp, onFling, onLongPress, onDoubleTap এবং অন্যান্য স্ট্যান্ডার্ড জেসচার শনাক্তকরণ সহজ করে।
private val gestureDetector = GestureDetector(
context, object : GestureDetector.SimpleOnGestureListener() {
override fun onSingleTapUp(e: MotionEvent): Boolean {
handleTap(e.x, e.y)
return true
}
})
override fun onTouchEvent(event: MotionEvent): Boolean {
val handled = gestureDetector.onTouchEvent(event)
when (event.action) {
MotionEvent.ACTION_MOVE -> {
currentX = event.x
currentY = event.y
invalidate()
return true
}
}
return handled || super.onTouchEvent(event)
}
হিট-টেস্টিং — নির্ধারণ করা যে স্পর্শটি View-এর কোনো নির্দিষ্ট এলাকায় পড়েছে কিনা। আয়তাকার এলাকার জন্য, Rect.contains(x, y) ব্যবহার করুন। বৃত্তের জন্য — কেন্দ্র থেকে দূরত্ব পরীক্ষা: sqrt(dx^2 + dy^2) < radius। নির্বিচারে আকারের জন্য — Path.op() বা Region.contains()।
মাল্টি-টাচ MotionEvent.getPointerCount() এবং getPointerId(index)-এর মাধ্যমে পরিচালিত হয়। প্রতিটি আঙুল একটি অনন্য ID পায় যা ACTION_DOWN থেকে ACTION_POINTER_UP পর্যন্ত সংরক্ষিত থাকে। ACTION_MOVE-এ সমস্ত সক্রিয় আঙুলের ডেটা থাকতে পারে — গতি ইন্টারপোলেশনের জন্য getHistoricalX/Y ব্যবহার করুন।
Custom View পারফরম্যান্সের বাধা হয়ে দাঁড়াতে পারে যদি onDraw ঘন ঘন কল করা হয় (অ্যানিমেশন, স্ক্রোল) বা এতে ভারী অপারেশন থাকে। Android কাস্টম উপাদান রেন্ডারিং অপ্টিমাইজ করার জন্য বেশ কয়েকটি প্রক্রিয়া প্রদান করে।
View.setLayerType View রেন্ডারিংকে সফটওয়্যার (LAYER_TYPE_SOFTWARE) বা হার্ডওয়্যার (LAYER_TYPE_HARDWARE) লেয়ারে পরিবর্তন করার অনুমতি দেয়। সফটওয়্যার লেয়ার জটিল Canvas গ্রাফিক্সের জন্য উপযোগী যা হার্ডওয়্যার এক্সিলারেশন দ্বারা সমর্থিত নয় — যেমন drawTextOnPath বা জটিল Path efekt।
invalidate(Rect) পুরো উপাদানের পরিবর্তে শুধুমাত্র View-এর নির্দিষ্ট এলাকা পুনরাঙ্কন করে। এটি বড় Custom Views (গ্রাফিক্স, মানচিত্র, অঙ্কন ক্যানভাস) এর জন্য গুরুত্বপূর্ণ, যেখানে প্রতিটি ফ্রেমের সম্পূর্ণ পুনরাঙ্কন FPS হ্রাস করে। vsync সিঙ্ক্রোনাইজেশনের জন্য postInvalidateOnAnimation() ব্যবহার করুন।
class EfficientCustomView
@JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null
) : View(context, attrs) {
// কনস্ট্রাক্টরে একবার তৈরি অবজেক্ট
private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
private val rect = Rect()
private val position = PointF()
fun updatePosition(x: Float, y: Float) {
position.set(x, y)
// শুধুমাত্র বিন্দুর চারপাশের এলাকা পুনরাঙ্কন করুন
rect.set(
(x - 10).toInt(),
(y - 10).toInt(),
(x + 10).toInt(),
(y + 10).toInt()
)
invalidate(rect)
}
}
হার্ডওয়্যার এক্সিলারেশন Android 3.0+ (API 11+) এ ডিফল্টভাবে সক্রিয়। Canvas অপারেশন drawCircle, drawRect, drawBitmap হার্ডওয়্যার এক্সেলারেটেড এবং GPU-তে সম্পাদিত হয়। তবে, drawTextOnPath, drawVertices, জটিল ক্লিপিং অপারেশন এক্সেলারেটেড নয় — সেগুলির জন্য LAYER_TYPE_SOFTWARE ব্যবহার করুন।
Android Performance Patterns (Google, 2023) অনুসারে, Custom View ল্যাগের প্রধান কারণ — onDraw-এ বরাদ্দ (প্রতি ফ্রেমে অবজেক্ট তৈরি), একটি উপাদান পরিবর্তন হলে পুরো View পুনরাঙ্কন এবং স্থির বিষয়বস্তুর জন্য LayerType.HARDWARE-এর অনুপস্থিতি। এই তিনটি সমস্যা সমাধান করলে বেশিরভাগ পরিস্থিতিতে FPS 30 থেকে 60-এ বৃদ্ধি পায়।
| মেথড | উদ্দেশ্য | কল ফ্রিকোয়েন্সি |
|---|---|---|
| onDraw | View বিষয়বস্তু অঙ্কন | প্রতি invalidate()-এ |
| onMeasure | View আকার নির্ধারণ | লেআউট পরিবর্তনে |
| onLayout | চাইল্ড View অবস্থান | onMeasure-এর পরে |
| onTouchEvent | টাচ হ্যান্ডলিং | প্রতি স্পর্শে |
| onSizeChanged | আকার পরিবর্তনে প্রতিক্রিয়া | প্রথম লেআউট ও পরিবর্তনে |
সচরাচর জিজ্ঞাসিত প্রশ্ন
Custom View (Canvas API) ন্যায্য যখন সর্বোচ্চ কাস্টম অঙ্কন পারফরম্যান্স প্রয়োজন (গ্রাফিক্স, ভিডিও এডিটর, মানচিত্র), বিদ্যমান View-ভিত্তিক কোডের সাথে একীকরণ বা API 21-এর নিচে Android সংস্করণের সমর্থন। Jetpack Compose অধিকাংশ নতুন প্রকল্পের জন্য একটি আধুনিক পদ্ধতি, যা ডিক্লারেটিভ UI ব্যবহার করে এবং গতিশীল ইন্টারফেসের জন্য বেশি উপযুক্ত।
হ্যাঁ, যদি View XML লেআউটে wrap_content ব্যবহার করে। onMeasure ছাড়া, wrap_content match_parent-এর মতো আচরণ করবে কারণ ডিফল্ট View.onMeasure বাস্তবায়ন ডিফল্ট আকার সেট করে না। যদি View-এর সবসময় নির্দিষ্ট আকার বা match_parent থাকে, তাহলে onMeasure ওভাররাইড না করলেও চলে।
View বৈশিষ্ট্য (রং, ব্যাসার্ধ, অবস্থান) পরিবর্তন করতে ValueAnimator বা ObjectAnimator ব্যবহার করুন এবং পুনরাঙ্কনের জন্য অ্যানিমেশন কলব্যাকে invalidate() কল করুন। ValueAnimator প্রধান থ্রেডে চলে এবং vsync-এর সাথে সিঙ্ক্রোনাইজড। জটিল অ্যানিমেশন (পদার্থবিদ্যা, কণা) এর জন্য, Choreographer.FrameCallback বা Android Animation Framework ব্যবহার করুন।
onTouchEvent-এ, আঙুলের সংখ্যা নির্ধারণ করতে MotionEvent.getPointerCount() এবং প্রতিটি আঙুল অনন্য ID দ্বারা ট্র্যাক করতে getPointerId(i) ব্যবহার করুন। ACTION_POINTER_DOWN / ACTION_POINTER_UP — আঙুল যোগ/অপসারণের ঘটনা। পিঞ্চ-টু-জুমের জন্য, onTouchEvent-এর সাথে ScaleGestureDetector ব্যবহার করুন — এটি পিঞ্চ শনাক্তকরণ সহজ করে।
হ্যাঁ, AndroidView-এর মাধ্যমে — একটি composable ফাংশন যা View-ভিত্তিক উপাদানকে Compose শ্রেণিবিন্যাসে এম্বেড করে। AndroidView View তৈরি করার জন্য একটি ফ্যাক্টরি এবং অবস্থা সিঙ্ক্রোনাইজেশনের জন্য update কলব্যাক গ্রহণ করে। এটি সম্পূর্ণ পুনর্লিখন ছাড়াই নতুন Compose প্রকল্পে বিদ্যমান Custom Views পুনঃব্যবহারের অনুমতি দেয়।
সারাংশ
আমরা একটি মোবাইল অ্যাপ্লিকেশন টার্নকি তৈরি করব
IT Sectr 2017 সাল থেকে স্টার্টআপ এবং ব্যবসার জন্য iOS এবং Android অ্যাপ্লিকেশন তৈরি করে। আমরা আপনাকে পরামর্শ দেব এবং সেরা সমাধান প্রস্তাব করব।
আরও পড়ুন