onDraw() Android SDK में View क्लास का एक महत्वपूर्ण मेथड है, जो किसी व्यू की सामग्री को ड्रा करने के लिए जिम्मेदार है। सिस्टम onDraw को View की उपस्थिति के हर अपडेट पर कॉल करता है — शुरुआती रेंडरिंग के दौरान, invalidate() के बाद या आयाम बदलने पर। Android Developers Documentation (2026) के अनुसार, Canvas API का उपयोग करके onDraw को ओवरराइड करना Android एप्लिकेशनों में कस्टम ग्राफिक्स बनाने का प्राथमिक तरीका है।
मुख्य बातें
onDraw(Canvas canvas) android.view.View क्लास का एक protected मेथड है, जिसे Android सिस्टम तब कॉल करता है जब View को खींचने की आवश्यकता होती है। डेवलपर कस्टम ड्राइंग को लागू करने के लिए इस मेथड को ओवरराइड करता है: आकार, टेक्स्ट, छवियां, ग्रैडिएंट, एनिमेशन। Canvas पैरामीटर एक ग्राफिकल सतह है जिस पर सभी ड्राइंग ओपरेशन किए जाते हैं।
मेथड पर हस्ताक्षर सरल है: override fun onDraw(canvas: Canvas)। इस मेथड के अंदर नए ऑब्जेक्ट नहीं बनाने चाहिए (GC के बोझ से बचने के लिए), लंबे ओपरेशन नहीं करने चाहिए या नेटवर्क के साथ काम नहीं करना चाहिए। View अपने लाइफसाइकल में onLayout (स्थिति गणना) और dispatchDraw (चाइल्ड एलिमेंट खींचना) के बीच onDraw मेथड को भी शामिल करता है।
View से इनहैरिट करने पर, कोई भी सामग्री प्रदर्शित करने के लिए onDraw को ओवरराइड करना अनिवार्य है — अन्यथा View खाली होगी। मौजूदा उपवर्गों (TextView, ImageView, Button) से इनहैरिट करने पर, onDraw को ओवरराइड करना ऐच्छिक है और मानक सामग्री के ऊपर अतिरिक्त ग्राफिक्स जोड़ने के लिए उपयोग किया जाता है।
Android मुख्य थ्रड (UI thread) से बुलाए गए सख्त रूप से परिभाषित अनुक्रम में View ड्राइंग करता है। पूर्ण साइकल में तीन चरण होते हैं: measure (आयाम मापना), layout (स्थिति गणना) और draw (रेंडरिंग)। onDraw draw चरण का हिस्सा है और View के आयाम और स्थिति निर्धारित होने के बाद बुलाया जाता है।
draw(Canvas canvas) View का एक सार्वजनिक मेथड है जो onDraw और dispatchDraw को कॉल करता है। draw मेथड को ओवरराइड करने के लिए डिजाइन नहीं किया गया है — इसके बजाय, onDraw को ओवरराइड किया जाता है। वर्तमान View के लिए onDraw निष्पादित होने के बाद, dispatchDraw को बुलाया जाता है, जो सभी चाइल्ड View के लिए draw को पुनरावर्ती रूप से बुलाता है (यदि वर्तमान View एक ViewGroup कंटेनर है)।
onDraw निम्नलिखित स्थितियों में बुलाया जाता है: 1) View पहली बार दिखाई देती है (setVisibility(VISIBLE) के बाद); 2) एप्लिकेशन इस View या इसके पैरेंट पर invalidate() कॉल करता है; 3) View का आकार बदलता है (नया layout); 4) View से संबंधित एनिमेशन बदलता है (ValueAnimator या ObjectAnimator के माध्यम से); 5) View की स्थिति बदलती है (pressed, focused, enabled)।
Android 3.0 (API 11) से, अधिकांश Canvas ओपरेशन GPU के माध्यम से हार्डवेयर एक्सेलरेटेड होते हैं। डेवलपर canvas.isHardwareAccelerated() के माध्यम से जाँच सकता है कि एक्सेलरेशन सक्षम है या नहीं। हार्डवेयर एक्सेलरेशन के साथ, कुछ Canvas ओपरेशन समर्थित नहीं होते (जैसे कि गैर-आयताकार क्षेत्र के साथ clipPath) और उन्हें अनुदेशित किया जा सकता है या वे त्रुटियाँ पैदा कर सकते हैं।
Canvas ड्राइंग मेथड्स का एक समृद्ध सेट प्रदान करता है। सभी मेथड वर्तमान निर्देशांक प्रणाली में काम करते हैं, जिसे रूपांतरणों (translate, rotate, scale, skew) के माध्यम से बदला जा सकता है। प्रत्येक मेथड एक Paint ऑब्जेक्ट स्वीकार करता है जो रंग, शैली, रेखा मोटाई, प्रभाव और एंटी-एलियसिंग को परिभाषित करता है।
| Canvas मेथड | उद्देश्य | Paint के मुख्य पैरामीटर |
|---|---|---|
| drawCircle | वृत्त खींचना | centerX, centerY, radius, paint |
| drawRect | आयत खींचना | left, top, right, bottom, paint |
| drawLine | रेखा खींचना | startX, startY, endX, endY, paint |
| drawPath | मनमाना पथ खींचना | path, paint |
| drawBitmap | बिटमैप छवि खींचना | bitmap, srcRect, dstRect, paint |
| drawText | टेक्स्ट खींचना | text, x, y, paint |
| drawArc | चाप या सेक्टर खींचना | ovalRect, startAngle, sweepAngle, useCenter, paint |
| drawOval | अंडाकार खींचना | ovalRect, paint |
Paint ड्राइंग शैली को परिभाषित करता है: रंग (setColor), रेखा मोटाई (setStrokeWidth), भरने की शैली (setStyle — FILL, STROKE, FILL_AND_STROKE), एंटी-एलियसिंग (setAntiAlias), प्रभाव (setShadowLayer, setMaskFilter) और ग्रैडिएंट (setShader)। Paint को View कंस्ट्रक्टर में एक बार बनाने और कॉन्फ़िगर करने की अनुशंसा की जाती है, onDraw के अंदर नहीं, ताकि ड्राइंग लूप में ऑब्जेक्ट निर्माण से बचा जा सके।
Canvas एक निर्देशांक प्रणाली का उपयोग करता है जहाँ X-अक्ष दाएँ और Y-अक्ष नीचे की ओर इशारा करता है। मूल बिंदु (0, 0) View का ओपरी बाएँ कोना है। draw मेथड कॉल करने से पहले, डेवलपर canvas.translate(dx, dy), canvas.rotate(degrees, px, py), canvas.scale(sx, sy, px, py) के माध्यम से निर्देशांक प्रणाली को बदल सकता है। सभी बाद के ड्राइंग ओपरेशन रूपांतरित प्रणाली में किए जाएँगे।
onDraw मुख्य UI थ्रड पर निष्पादित होता है, और इसकी अवधि सीधे फ्रेम दर (fps) को प्रभावित करती है। 60 fps बनाए रखने के लिए, onDraw मेथड को 16 मिलीसेकंड के भीतर पूरा होना चाहिए। कोई भी मन्दी फ्रेम ड्रॉप (jank) का कारण बनती है, जो दृश्य रूप से एनिमेशन हॅटकने या स्क्रॉल में देरी के रूप में दिखाई देती है।
मुख्य नियम onDraw ऑप्टिमाइजेशन के लिए: 1) onDraw के अंदर ऑब्जेक्ट न बनाएं — सभी Paint, Path, Rect ऑब्जेक्ट क्लास फ़ील्ड होने चाहिए; 2) मेमरी आवंटित (new) न करें — यह गार्बेज कलेक्शन (GC) और फ्रेम ड्रॉप का कारण बनता है; 3) जटिल ओपरेशनों को Bitmap.createBitmap के माध्यम से Bitmap में कैश करें और तैयार Bitmap को ड्रा करें; 4) I/O ओपरेशन या संसाधन लोडिंग न करें।
clipRect ड्राइंग क्षेत्र को एक निर्दिष्ट आयत तक सीमित करता है। इसका उपयोग Canvas को दृश्य क्षेत्र के बाहर पिक्सल खींचने में समय बर्बाद करने से रोकने के लिए किया जाता है। यह विशेष रूप से बड़ी सूचियों को स्क्रॉल करते समय या जूम करते समय प्रभावी होता है: clipRect को दृश्य क्षेत्र (dirty rect) पर सेट करें, और Canvas केवल वही खींचेगा जो दिखाई देता है।
canvas.save() Canvas की वर्तमान स्थिति (रूपांतरण और clip) को एक स्टैक पर सहेज करता है। canvas.restore() अंतिम सहेजित स्थिति को पुनर्स्थापित करता है। यह अस्थायी रूपांतरणों के लिए आवश्यक है: स्थिति सहेज करें, translate/rotate लागू करें, ड्रा करें, फिर पुनर्स्थापित करें। save और restore हल्के होते हैं और उचित उपयोग (10–15 से कम नेस्टिंग स्तर) पर प्रदर्शन को प्रभावित नहीं करते।
invalidate() View का एक मेथड है जो सिस्टम को बताता है कि View की सामग्री पुरानी हो गई है और उसे दोबारा खींचने की आवश्यकता है। invalidate कॉल करने से एक नया draw साइकल शुरू होता है जिसमें onDraw बुलाया जाएगा। invalidate को केवल UI थ्रड (मुख्य थ्रड) से ही बुलाया जाना चाहिए। पृष्ठभूमि थ्रड से बुलाने के लिए postInvalidate का उपयोग किया जाता है।
postInvalidate() invalidate का एक थ्रड-सेफ संस्करण है जो Handler के माध्यम से मुख्य थ्रड की क्यू में पुनर्लेखन अनुरोध भेजता है। इसका उपयोग पृष्ठभूमि कार्यों में किया जाता है जब डेटा लोडिंग या गणनाओं के बाद View को अपडेट करने की आवश्यकता होती है। इसे UI सिंक्रोनिजेशन के लिए runOnUiThread के साथ जोड़ा जा सकता है।
आंशिक पुनर्लेखन के लिए, invalidate(Rect dirty) या invalidate(int l, int t, int r, int b) का उपयोग करें। यह सिस्टम को बताता है कि पूरे View में नहीं, बल्कि केवल निर्दिष्ट क्षेत्र बदला है। Android केवल dirty rect को पुनर्लेखित करके ड्राइंग को ऑप्टिमइज़ करता है, जो आंशिक अपडेट के साथ एनिमेशन में प्रदर्शन में काफी सुधार करता है।
आइए Kotlin का उपयोग करके Android में कस्टम View बनाने के लिए onDraw को ओवरराइड करने के व्यावहारिक उदाहरण देखें।
यह उदाहरण एनिमेटेड भरने के साथ एक वृत्ताकार प्रगति संकेतक बनाता है। onDraw दो वृत्त खींचता है: एक ग्रे पृष्ठभूमि वृत्त और एक रंगीन भरने वाला वृत्त जिसका चाप वर्तमान प्रगति द्वारा निर्धारित होता है। setProgress में invalidate कॉल करने से मान बदलने पर पुनर्लेखन सुनिश्चित होता है।
class CircularProgressView
@JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null
) : View(context, attrs) {
private val bgPaint = Paint().apply {
style = Paint.Style.STROKE
strokeWidth = 12f
color = Color.parseColor("#E0E0E0")
isAntiAlias = true
}
private val progressPaint = Paint().apply {
style = Paint.Style.STROKE
strokeWidth = 12f
color = Color.BLUE
isAntiAlias = true
strokeCap = Paint.Cap.ROUND
}
var progress: Float = 0f
set(value) {
field = value.coerceIn(0f, 1f)
invalidate()
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
val size = minOf(width, height) - 24f
val oval = RectF(12f, 12f,
12f + size, 12f + size)
canvas.drawArc(oval, 270f, 360f, false, bgPaint)
canvas.drawArc(oval, 270f,
progress * 360f,
false, progressPaint)
}
}
यह उदाहरण ग्रैडिएंट के साथ एक आयत और उसके ऊपर टेक्स्ट खींचता है। ग्रैडिएंट LinearGradient (Shader) के माध्यम से बनाया जाता है, और टेक्स्ट drawText के माध्यम से। दोनों Paint ऑब्जेक्ट कंस्ट्रक्टर में एक बार बनाए जाते हैं, जबकि onDraw केवल View की वर्तमान चौड़ाई के आधार पर आयत का आकार अपडेट करता है।
class GradientTextView(context: Context)
: View(context) {
private val shaderPaint = Paint().apply {
isAntiAlias = true
}
private val textPaint = Paint().apply {
color = Color.WHITE
textSize = 48f
isAntiAlias = true
textAlign = Paint.Align.CENTER
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
val gradient = LinearGradient(
0f, 0f, width.toFloat(), 0f,
Color.parseColor("#FF6B35"),
Color.parseColor("#FF0040"),
Shader.TileMode.CLAMP
)
shaderPaint.shader = gradient
canvas.drawRect(0f, 0f,
width.toFloat(),
height.toFloat(), shaderPaint)
val xCenter = width / 2f
val yCenter = height / 2f
val yOffset = (textPaint.descent() +
textPaint.ascent()) / 2f
canvas.drawText("Hello from Canvas",
xCenter, yCenter - yOffset,
textPaint)
}
}
यह उदाहरण एक एनिमेटेड घड़ी बनाता है जो हर 1000 मिलीसेकंड में ड्राइंग को अपडेट करती है। onDraw के अंदर postDelayed एक सेकंड के बाद invalidate को कॉल करता है, एक अनंत पुनर्लेखन लूप बनाता है। घड़ी की सुईयां अलग-अलग लंबाई और मोटाई के साथ drawLine के माध्यम से खींची जाती हैं।
class AnalogClockView(context: Context)
: View(context) {
private val handPaint = Paint().apply {
style = Paint.Style.STROKE
isAntiAlias = true
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
val cx = width / 2f
val cy = height / 2f
val radius = minOf(cx, cy) - 20f
val now = Calendar.getInstance()
val hours = now.get(Calendar.HOUR) % 12
val minutes = now.get(Calendar.MINUTE)
val seconds = now.get(Calendar.SECOND)
val secondAngle = Math.toRadians(
seconds * 6f - 90f)
handPaint.strokeWidth = 2f
handPaint.color = Color.RED
canvas.drawLine(cx, cy,
cx + cos(secondAngle) * radius * 0.8f,
cy + sin(secondAngle) * radius * 0.8f,
handPaint)
postDelayed({ invalidate() }, 1000)
}
}
अक्सर पूछे जाने वाले प्रश्न
नहीं, onDraw को सीधे नहीं बुलाना चाहिए। पुनर्लेखन का अनुरोध करने के लिए, invalidate() (UI थ्रड से) या postInvalidate() (पृष्ठभूमि थ्रड से) का उपयोग करें। सिस्टम तय करेगा कि onDraw कब बुलाना है और एकाधिक invalidate कॉल को एक पास में विलय करके ड्राइंग को ऑप्टिमइज़ करेगा।
कारण: View छिपी हुई है (setVisibility(GONE) या INVISIBLE); View को Window पदानुक्रम में नहीं जोड़ा गया; View का आकार 0 है (चौड़ाई या ऊंचाई = 0); setWillNotDraw(true) बुलाया गया है (डिफ़ॉल्ट रूप से ViewGroup के लिए true)। डिबग करते समय इनमें से प्रत्येक बिंदु की जाँच करें।
60 fps पर, onDraw प्रति सेकंड 60 बार तक बुलाया जा सकता है। 120 fps पर (120 Hz डिस्प्ले वाले उपकरणों पर) — 120 बार तक। प्रत्येक कॉल लगभग 8–16 मिलीसेकंड में पूरा होना चाहिए। ObjectAnimator या ValueAnimator के माध्यम से सक्रिय एनिमेशन के दौरान, आवृत्ति एनिमेशन फ्रेम की संख्या द्वारा निर्धारित की जाती है।
onDraw View की स्वयं की सामग्री खींचने के लिए जिम्मेदार है। dispatchDraw चाइल्ड View (ViewGroup में) खींचने के लिए जिम्मेदार है। dispatchDraw onDraw के बाद बुलाया जाता है और सभी चाइल्ड एलिमेंट के लिए पुनरावर्ती रूप से जाता है। एक सामान्य View (कोई ViewGroup नहीं) के लिए, dispatchDraw खाली होता है।
हाँ, Canvas को Canvas(bitmap) के माध्यम से Bitmap के लिए बनाया जा सकता है और किसी भी थ्रड पर उस पर ड्रा किया जा सकता है। इसका उपयोग पृष्ठभूमि में जटिल ग्राफिक्स को प्री-रेंडर करने के लिए किया जाता है। हालाँकि, onDraw को पास किया गया Canvas सिस्टम का है और इसे केवल onDraw मेथड के अंदर UI थ्रड पर ही इस्तेमाल किया जा सकता है।
सारांश
हम एक मोबाइल एप्लिकेशन टर्नकी विकसित करेंगे
IT Sectr 2017 से स्टार्टअप और व्यवसायों के लिए iOS और Android एप्लिकेशन बनाता है। हम आपको सलाह देंगे और सर्वोत्तम समाधान प्रस्तावित करेंगे।
यह भी पढ़ें