कैनवस ऑब्जेक्ट पर ड्रॉइंग करना

यह कोडलैब, Kotlin में ऐडवांस Android कोर्स का हिस्सा है. अगर इस कोर्स के कोडलैब को क्रम से पूरा किया जाता है, तो आपको सबसे ज़्यादा फ़ायदा मिलेगा. हालांकि, ऐसा करना ज़रूरी नहीं है. कोर्स के सभी कोडलब, Advanced Android in Kotlin कोडलब के लैंडिंग पेज पर दिए गए हैं.

परिचय

Android में, व्यू में कस्टम 2D ग्राफ़िक और ऐनिमेशन लागू करने के लिए, कई तकनीकें उपलब्ध हैं.

ड्रॉएबल का इस्तेमाल करने के अलावा, Canvas क्लास के ड्रॉइंग के तरीकों का इस्तेमाल करके 2D ड्रॉइंग बनाई जा सकती हैं. Canvas, ड्रॉइंग के लिए 2D कैनवस होता है. इसमें ड्रॉइंग के तरीके उपलब्ध होते हैं. यह तब काम आता है, जब आपके ऐप्लिकेशन को नियमित तौर पर खुद को फिर से ड्रॉ करने की ज़रूरत होती है. ऐसा इसलिए, क्योंकि समय के साथ उपयोगकर्ता को दिखने वाली चीज़ें बदल जाती हैं. इस कोडलैब में, आपको View में दिखने वाले कैनवस पर बनाने और ड्रॉ करने का तरीका बताया गया है.

कैनवस पर ये कार्रवाइयां की जा सकती हैं:

  • पूरे कैनवस में रंग भरें.
  • आयत, चाप, और पाथ जैसी आकृतियां बनाएं. इन्हें Paint ऑब्जेक्ट में तय किए गए स्टाइल के मुताबिक बनाया जाता है. Paint ऑब्जेक्ट में, ज्यामिति (जैसे कि लाइन, रेक्टैंगल, ओवल, और पाथ) को ड्रा करने के तरीके या टेक्स्ट के टाइपफ़ेस के बारे में स्टाइल और रंग की जानकारी होती है.
  • ट्रांसफ़ॉर्मेशन लागू करें. जैसे, अनुवाद, स्केलिंग या कस्टम ट्रांसफ़ॉर्मेशन.
  • क्लिप करें. इसका मतलब है कि कैनवस पर कोई शेप या पाथ लागू करके, उसके दिखने वाले हिस्सों को तय करें.

Android में ड्राइंग के बारे में जानकारी (बहुत आसान शब्दों में!)

Android या किसी अन्य मॉडर्न सिस्टम में ड्रॉइंग करना एक जटिल प्रोसेस है. इसमें कई तरह की लेयर और हार्डवेयर तक ऑप्टिमाइज़ेशन शामिल होते हैं. Android कैसे ड्रॉ करता है, यह एक दिलचस्प विषय है. इसके बारे में काफ़ी कुछ लिखा जा चुका है. इसकी जानकारी इस कोडलैब के दायरे से बाहर है.

इस कोडलैब और इसके ऐप्लिकेशन के हिसाब से, इसे इस तरह से समझा जा सकता है. यह ऐप्लिकेशन, फ़ुल-स्क्रीन व्यू में दिखाने के लिए कैनवस का इस्तेमाल करता है.

  1. आपको ड्रॉ की जा रही चीज़ को दिखाने के लिए, एक व्यू की ज़रूरत होती है. यह Android सिस्टम की ओर से उपलब्ध कराए गए व्यू में से एक हो सकता है. इसके अलावा, इस कोडलैब में आपको एक कस्टम व्यू बनाना है. यह आपके ऐप्लिकेशन के लिए कॉन्टेंट व्यू के तौर पर काम करेगा (MyCanvasView).
  2. इस व्यू में, सभी व्यू की तरह ही अपना कैनवस (canvas) होता है.
  3. किसी व्यू के कैनवस पर ड्राइंग करने के लिए, उसके onDraw() तरीके को बदलें और उसके कैनवस पर ड्राइंग करें.
  4. ड्राइंग बनाते समय, आपको पहले से बनाई गई ड्राइंग को कैश मेमोरी में सेव करना होगा. आपके डेटा को कैश मेमोरी में सेव करने के कई तरीके हैं. इनमें से एक तरीका, बिटमैप (extraBitmap) में सेव करना है. दूसरा तरीका, कोऑर्डिनेट और निर्देशों के तौर पर बनाई गई चीज़ों का इतिहास सेव करना है.
  5. कैनवस ड्राइंग एपीआई का इस्तेमाल करके, अपने कैशिंग बिटमैप (extraBitmap) पर ड्रॉ करने के लिए, आपको अपने कैशिंग बिटमैप के लिए एक कैशिंग कैनवस (extraCanvas) बनाना होगा.
  6. इसके बाद, अपने कैशिंग कैनवस (extraCanvas) पर ड्रॉ करें. इससे कैशिंग बिटमैप (extraBitmap) पर ड्रॉ होता है.
  7. स्क्रीन पर ड्रॉ की गई हर चीज़ को दिखाने के लिए, व्यू के कैनवस (canvas) को कैशिंग बिटमैप (extraBitmap) ड्रॉ करने के लिए कहा जाता है.

आपको पहले से क्या पता होना चाहिए

  • Android Studio का इस्तेमाल करके, ऐक्टिविटी और बुनियादी लेआउट वाला ऐप्लिकेशन बनाने और उसे चलाने का तरीका.
  • इवेंट हैंडलर को व्यू से जोड़ने का तरीका.
  • कस्टम व्यू बनाने का तरीका.

आपको क्या सीखने को मिलेगा

  • उपयोगकर्ता के टच के जवाब में, Canvas बनाने और उस पर ड्रॉ करने का तरीका.

आपको क्या करना होगा

  • ऐसा ऐप्लिकेशन बनाएं जो उपयोगकर्ता के स्क्रीन को छूने पर, स्क्रीन पर लाइनें बनाए.
  • मोशन इवेंट कैप्चर करें. इसके जवाब में, स्क्रीन पर फ़ुलस्क्रीन कस्टम व्यू में दिखने वाले कैनवस पर लाइनें बनाएं.

MiniPaint ऐप्लिकेशन, उपयोगकर्ता के टच के जवाब में लाइन दिखाने के लिए कस्टम व्यू का इस्तेमाल करता है. इसे यहां दिए गए स्क्रीनशॉट में दिखाया गया है.

पहला चरण. MiniPaint प्रोजेक्ट बनाना

  1. MiniPaint नाम का एक नया Kotlin प्रोजेक्ट बनाएं. इसके लिए, Empty Activity टेंप्लेट का इस्तेमाल करें.
  2. app/res/values/colors.xml फ़ाइल खोलें और ये दो रंग जोड़ें.
<color name="colorBackground">#FFFF5500</color>
<color name="colorPaint">#FFFFEB3B</color>
  1. styles.xml खोलें
  2. दी गई AppTheme स्टाइल के पैरंट में, DarkActionBar को NoActionBar से बदलें. इससे ऐक्शन बार हट जाता है, ताकि फ़ुलस्क्रीन पर ड्रॉ किया जा सके.
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">

दूसरा चरण. MyCanvasView क्लास बनाएं

इस चरण में, ड्रॉइंग के लिए कस्टम व्यू MyCanvasView बनाया जाता है.

  1. app/java/com.example.android.minipaint पैकेज में, नई > Kotlin फ़ाइल/क्लास बनाएं और उसका नाम MyCanvasView रखें.
  2. MyCanvasView क्लास को View क्लास से एक्सटेंड करें और context: Context में पास करें. सुझाई गई इंपोर्ट की कार्रवाइयों को स्वीकार करें.
import android.content.Context
import android.view.View

class MyCanvasView(context: Context) : View(context) {
}

तीसरा चरण. MyCanvasView को कॉन्टेंट व्यू के तौर पर सेट करना

MyCanvasView में जो भी ड्रा किया जाएगा उसे दिखाने के लिए, आपको इसे MainActivity के कॉन्टेंट व्यू के तौर पर सेट करना होगा.

  1. strings.xml खोलें और व्यू के कॉन्टेंट के ब्यौरे के लिए इस्तेमाल की जाने वाली स्ट्रिंग तय करें.
<string name="canvasContentDescription">Mini Paint is a simple line drawing app.
   Drag your fingers to draw. Rotate the phone to clear.</string>
  1. MainActivity.kt खोलें
  2. onCreate() में, setContentView(R.layout.activity_main) को मिटाएं.
  3. MyCanvasView का इंस्टेंस बनाएं.
val myCanvasView = MyCanvasView(this)
  1. इसके बाद, myCanvasView के लेआउट के लिए फ़ुल स्क्रीन का अनुरोध करें. इसके लिए, myCanvasView पर SYSTEM_UI_FLAG_FULLSCREEN फ़्लैग सेट करें. इस तरह, व्यू पूरी स्क्रीन पर दिखता है.
myCanvasView.systemUiVisibility = SYSTEM_UI_FLAG_FULLSCREEN
  1. कॉन्टेंट का ब्यौरा जोड़ें.
myCanvasView.contentDescription = getString(R.string.canvasContentDescription)
  1. इसके बाद, कॉन्टेंट व्यू को myCanvasView पर सेट करें.
setContentView(myCanvasView)
  1. अपना ऐप्लिकेशन चलाएं. आपको पूरी तरह से सफ़ेद स्क्रीन दिखेगी, क्योंकि कैनवस का कोई साइज़ नहीं है और आपने अभी तक कुछ भी नहीं बनाया है.

पहला चरण. onSizeChanged() को ओवरराइड करना

जब भी व्यू का साइज़ बदलता है, तब Android सिस्टम onSizeChanged() तरीके को कॉल करता है. व्यू की शुरुआत बिना किसी साइज़ के होती है. इसलिए, गतिविधि के पहली बार व्यू को बनाने और बढ़ाने के बाद, व्यू का onSizeChanged() तरीका भी कॉल किया जाता है. इसलिए, व्यू का कैनवस बनाने और सेट अप करने के लिए, यह onSizeChanged() तरीका सबसे सही है.

  1. MyCanvasView में, क्लास लेवल पर कैनवस और बिटमैप के लिए वैरिएबल तय करें. उन्हें extraCanvas और extraBitmap को कॉल करो. ये आपके बिटमैप और कैनवस हैं. इनका इस्तेमाल, पहले से ड्रॉ की गई चीज़ों को कैश मेमोरी में सेव करने के लिए किया जाता है.
private lateinit var extraCanvas: Canvas
private lateinit var extraBitmap: Bitmap
  1. कैनवस के बैकग्राउंड के रंग के लिए, क्लास लेवल का वैरिएबल backgroundColor तय करें. साथ ही, इसे पहले से तय किए गए colorBackground पर सेट करें.
private val backgroundColor = ResourcesCompat.getColor(resources, R.color.colorBackground, null)
  1. MyCanvasView में, onSizeChanged() तरीके को बदलें. इस कॉलबैक तरीके को Android सिस्टम, स्क्रीन के बदले गए डाइमेंशन के साथ कॉल करता है. इसका मतलब है कि नई चौड़ाई और ऊंचाई (बदलने के लिए) और पुरानी चौड़ाई और ऊंचाई (बदलने के लिए).
override fun onSizeChanged(width: Int, height: Int, oldWidth: Int, oldHeight: Int) {
   super.onSizeChanged(width, height, oldWidth, oldHeight)
}
  1. onSizeChanged() के अंदर, नई चौड़ाई और ऊंचाई के साथ Bitmap का एक इंस्टेंस बनाएं. यह स्क्रीन का साइज़ है. इसके बाद, इसे extraBitmap को असाइन करें. तीसरा आर्ग्युमेंट, बिटमैप कलर कॉन्फ़िगरेशन है. ARGB_8888 हर रंग को 4 बाइट में सेव करता है. हमारा सुझाव है कि आप इसका इस्तेमाल करें.
extraBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
  1. extraBitmap से Canvas इंस्टेंस बनाएं और उसे extraCanvas को असाइन करें.
 extraCanvas = Canvas(extraBitmap)
  1. उस बैकग्राउंड का रंग तय करें जिसमें extraCanvas को भरना है.
extraCanvas.drawColor(backgroundColor)
  1. onSizeChanged() को देखने पर पता चलता है कि फ़ंक्शन के हर बार एक्ज़ीक्यूट होने पर, एक नया बिटमैप और कैनवस बनाया जाता है. आपको एक नए बिटमैप की ज़रूरत होगी, क्योंकि साइज़ बदल गया है. हालांकि, यह मेमोरी लीक है. इससे पुराने बिटमैप मौजूद रहते हैं. इस समस्या को ठीक करने के लिए, super को कॉल करने के ठीक बाद यह कोड जोड़कर, extraBitmap को रीसाइकल करें.
if (::extraBitmap.isInitialized) extraBitmap.recycle()

दूसरा चरण. onDraw() को ओवरराइड करना

MyCanvasView के लिए ड्रॉइंग से जुड़ा सारा काम onDraw() में होता है.

शुरू करने के लिए, कैनवस को डिसप्ले करें. साथ ही, स्क्रीन को उस बैकग्राउंड कलर से भरें जिसे आपने onSizeChanged() में सेट किया है.

  1. onDraw() को बदलें और व्यू से जुड़े कैनवस पर, कैश किए गए extraBitmap का कॉन्टेंट बनाएं. drawBitmap() Canvas तरीके के कई वर्शन उपलब्ध हैं. इस कोड में, आपको बिटमैप, सबसे ऊपर बाएं कोने के x और y कोऑर्डिनेट (पिक्सल में), और Paint के लिए null देना होता है. ऐसा इसलिए, क्योंकि इसे बाद में सेट किया जाएगा.
override fun onDraw(canvas: Canvas) {
   super.onDraw(canvas)
canvas.drawBitmap(extraBitmap, 0f, 0f, null)
}


ध्यान दें कि onDraw() को पास किया गया कैनवस, उस कैनवस से अलग होता है जिसे सिस्टम, बिटमैप दिखाने के लिए इस्तेमाल करता है. यह उस कैनवस से भी अलग होता है जिसे आपने onSizeChanged() तरीके में बनाया था और जिसका इस्तेमाल आपने बिटमैप पर ड्रॉ करने के लिए किया था.

  1. अपना ऐप्लिकेशन चलाएं. आपको पूरी स्क्रीन पर, तय किया गया बैकग्राउंड कलर दिखना चाहिए.

ड्रॉ करने के लिए, आपको Paint ऑब्जेक्ट की ज़रूरत होती है. यह ऑब्जेक्ट बताता है कि ड्रॉ करते समय चीज़ों को कैसे स्टाइल किया जाता है. साथ ही, आपको Path ऑब्जेक्ट की भी ज़रूरत होती है. यह ऑब्जेक्ट बताता है कि क्या ड्रॉ किया जा रहा है.

पहला चरण. Paint ऑब्जेक्ट को शुरू करना

  1. MyCanvasView.kt में, सबसे ऊपर फ़ाइल लेवल पर, स्ट्रोक की चौड़ाई के लिए एक कॉन्स्टेंट तय करें.
private const val STROKE_WIDTH = 12f // has to be float
  1. MyCanvasView के क्लास लेवल पर, ड्रॉ करने के लिए रंग को होल्ड करने वाला drawColor वैरिएबल तय करें. साथ ही, इसे पहले से तय किए गए colorPaint रिसॉर्स से शुरू करें.
private val drawColor = ResourcesCompat.getColor(resources, R.color.colorPaint, null)
  1. नीचे दिए गए क्लास लेवल पर, Paint ऑब्जेक्ट के लिए paint वैरिएबल जोड़ें और उसे इस तरह से शुरू करें.
// Set up the paint with which to draw.
private val paint = Paint().apply {
   color = drawColor
   // Smooths out edges of what is drawn without affecting shape.
   isAntiAlias = true
   // Dithering affects how colors with higher-precision than the device are down-sampled.
   isDither = true
   style = Paint.Style.STROKE // default: FILL
   strokeJoin = Paint.Join.ROUND // default: MITER
   strokeCap = Paint.Cap.ROUND // default: BUTT
   strokeWidth = STROKE_WIDTH // default: Hairline-width (really thin)
}
  • paint का color, वह drawColor है जिसे आपने पहले तय किया था.
  • isAntiAlias से यह तय होता है कि एज स्मूदिंग लागू करनी है या नहीं. isAntiAlias को true पर सेट करने से, शेप पर असर डाले बिना, ड्रॉ की गई चीज़ के किनारों को स्मूद किया जाता है.
  • isDither, true होने पर, यह तय करता है कि डिवाइस की तुलना में ज़्यादा सटीक कलर वाले पिक्सल को डाउन-सैंपल कैसे किया जाए. उदाहरण के लिए, डिथरिंग एक ऐसा तरीका है जिससे इमेज के कलर रेंज को 256 (या इससे कम) रंगों तक कम किया जा सकता है.
  • style, स्ट्रोक पर की जाने वाली पेंटिंग का टाइप सेट करता है. स्ट्रोक का मतलब लाइन से है. Paint.Style से यह तय होता है कि ड्रॉ किए जा रहे प्रिमिटिव को भरा गया है, स्ट्रोक किया गया है या दोनों (एक ही रंग में). डिफ़ॉल्ट रूप से, पेंट को उस ऑब्जेक्ट में भरा जाता है जिस पर उसे लागू किया गया है. ("Fill" से शेप के अंदर का रंग बदलता है, जबकि "stroke" से उसके बॉर्डर का रंग बदलता है.)
  • Paint.Join के strokeJoin से यह तय होता है कि स्ट्रोक किए गए पाथ पर लाइनें और कर्व सेगमेंट कैसे जुड़ते हैं. डिफ़ॉल्ट वैल्यू MITER है.
  • strokeCap लाइन के आखिर में कैप का आकार सेट करता है. Paint.Cap स्ट्रोक की गई लाइनों और पाथ की शुरुआत और आखिर के बारे में बताता है. डिफ़ॉल्ट वैल्यू BUTT है.
  • strokeWidth से स्ट्रोक की चौड़ाई पिक्सल में तय की जाती है. डिफ़ॉल्ट रूप से, लाइन की चौड़ाई बहुत कम होती है. इसलिए, इसे STROKE_WIDTH पर सेट किया जाता है.

दूसरा चरण. पाथ ऑब्जेक्ट को शुरू करना

Path, उपयोगकर्ता की ओर से बनाई जा रही इमेज का पाथ है.

  1. MyCanvasView में, एक वैरिएबल path जोड़ें और उसे Path ऑब्जेक्ट के साथ शुरू करें. इससे स्क्रीन पर उपयोगकर्ता के टच को फ़ॉलो करते समय, ड्रॉ किए जा रहे पाथ को सेव किया जा सकेगा. Path के लिए android.graphics.Path इंपोर्ट करें.
private var path = Path()

पहला चरण. डिसप्ले पर होने वाली गतिविधियों के हिसाब से जवाब देना

जब भी उपयोगकर्ता डिसप्ले को छूता है, तब व्यू पर मौजूद onTouchEvent() तरीके को कॉल किया जाता है.

  1. MyCanvasView में, onTouchEvent() तरीके को बदलकर, पास किए गए event के x और y निर्देशांकों को कैश मेमोरी में सेव करें. इसके बाद, स्क्रीन पर टच करने, स्क्रीन पर इधर-उधर जाने, और स्क्रीन से टच हटाने के लिए, when एक्सप्रेशन का इस्तेमाल करें. ये इवेंट, स्क्रीन पर लाइन बनाने के लिए ज़रूरी होते हैं. हर इवेंट टाइप के लिए, यूटिलिटी मेथड को कॉल करें. जैसा कि यहां दिए गए कोड में दिखाया गया है. टच इवेंट की पूरी सूची के लिए, MotionEvent क्लास का दस्तावेज़ देखें.
override fun onTouchEvent(event: MotionEvent): Boolean {
   motionTouchEventX = event.x
   motionTouchEventY = event.y

   when (event.action) {
       MotionEvent.ACTION_DOWN -> touchStart()
       MotionEvent.ACTION_MOVE -> touchMove()
       MotionEvent.ACTION_UP -> touchUp()
   }
   return true
}
  1. क्लास लेवल पर, मौजूदा टच इवेंट (MotionEvent कोऑर्डिनेट) के x और y कोऑर्डिनेट को कैश मेमोरी में सेव करने के लिए, छूटे हुए motionTouchEventX और motionTouchEventY वैरिएबल जोड़ें. उन्हें 0f पर सेट करें.
private var motionTouchEventX = 0f
private var motionTouchEventY = 0f
  1. तीन फ़ंक्शन touchStart(), touchMove(), और touchUp() के लिए स्टब बनाएं.
private fun touchStart() {}

private fun touchMove() {}

private fun touchUp() {}
  1. आपका कोड बन जाएगा और चलेगा. हालांकि, आपको अब भी रंगीन बैकग्राउंड के अलावा कुछ और नहीं दिखेगा.

दूसरा चरण. touchStart() लागू करना

जब उपयोगकर्ता पहली बार स्क्रीन को टच करता है, तब इस तरीके का इस्तेमाल किया जाता है.

  1. क्लास लेवल पर, x और y की नई वैल्यू को कैश मेमोरी में सेव करने के लिए वैरिएबल जोड़ें. जब उपयोगकर्ता चलना बंद कर देता है और स्क्रीन से अपनी उंगली हटा लेता है, तो ये अगले पाथ (लाइन का अगला सेगमेंट) के शुरुआती पॉइंट होते हैं.
private var currentX = 0f
private var currentY = 0f
  1. touchStart() तरीके को इस तरह लागू करें. path को रीसेट करें, टच इवेंट के x-y कोऑर्डिनेट (motionTouchEventX और motionTouchEventY) पर जाएं, और उस वैल्यू को currentX और currentY असाइन करें.
private fun touchStart() {
   path.reset()
   path.moveTo(motionTouchEventX, motionTouchEventY)
   currentX = motionTouchEventX
   currentY = motionTouchEventY
}

तीसरा चरण. touchMove() लागू करना

  1. क्लास लेवल पर, touchTolerance वैरिएबल जोड़ें और इसे ViewConfiguration.get(context).scaledTouchSlop पर सेट करें.
private val touchTolerance = ViewConfiguration.get(context).scaledTouchSlop

पाथ का इस्तेमाल करने पर, हर पिक्सल को बनाने और हर बार डिसप्ले को रीफ़्रेश करने का अनुरोध करने की ज़रूरत नहीं होती. इसके बजाय, बेहतर परफ़ॉर्मेंस के लिए, पॉइंट के बीच के पाथ का अनुमान लगाया जा सकता है.

  • अगर उंगली को थोड़ा-बहुत ही घुमाया गया है, तो ड्रॉ करने की ज़रूरत नहीं है.
  • अगर उंगली को touchTolerance से कम दूरी तक ले जाया गया है, तो न खींचें.
  • scaledTouchSlop से, पिक्सल में वह दूरी मिलती है जो उपयोगकर्ता के टच को तय करनी होती है. इसके बाद, सिस्टम यह तय करता है कि उपयोगकर्ता स्क्रोल कर रहा है.
  1. touchMove() तरीके को तय करें. तय की गई दूरी (dx, dy) का हिसाब लगाएं, दो पॉइंट के बीच एक कर्व बनाएं और उसे path में सेव करें, currentX और currentY की मौजूदा संख्या को अपडेट करें, और path बनाएं. इसके बाद, अपडेट किए गए path के साथ स्क्रीन को फिर से रेंडर करने के लिए, invalidate() को कॉल करें.
private fun touchMove() {
   val dx = Math.abs(motionTouchEventX - currentX)
   val dy = Math.abs(motionTouchEventY - currentY)
   if (dx >= touchTolerance || dy >= touchTolerance) {
       // QuadTo() adds a quadratic bezier from the last point,
       // approaching control point (x1,y1), and ending at (x2,y2).
       path.quadTo(currentX, currentY, (motionTouchEventX + currentX) / 2, (motionTouchEventY + currentY) / 2)
       currentX = motionTouchEventX
       currentY = motionTouchEventY
       // Draw the path in the extra bitmap to cache it.
       extraCanvas.drawPath(path, paint)
   }
   invalidate()
}

इस तरीके के बारे में ज़्यादा जानकारी:

  1. तय की गई दूरी (dx, dy) का हिसाब लगाओ.
  2. अगर उंगली को टच टॉलरेंस से ज़्यादा दूरी तक ले जाया गया है, तो पाथ में एक सेगमेंट जोड़ें.
  3. अगले सेगमेंट का शुरुआती पॉइंट, इस सेगमेंट के एंडपॉइंट पर सेट करें.
  4. lineTo() के बजाय quadTo() का इस्तेमाल करके, बिना कोनों वाली लाइन आसानी से बनाई जा सकती है. बेज़ियर कर्व देखें.
  5. invalidate() को कॉल करें, ताकि व्यू को फिर से बनाया जा सके. इसके बाद, onDraw() को कॉल करें.

चौथा चरण: touchUp() लागू करना

जब उपयोगकर्ता अपनी उंगली हटा लेता है, तो पाथ को रीसेट करना होता है, ताकि उसे फिर से न बनाया जा सके. कुछ भी नहीं बनाया गया है, इसलिए अमान्य करने की ज़रूरत नहीं है.

  1. touchUp() वाला तरीका लागू करें.
private fun touchUp() {
   // Reset the path so it doesn't get drawn again.
   path.reset()
}
  1. अपना कोड चलाएं और स्क्रीन पर ड्रॉ करने के लिए, अपनी उंगली का इस्तेमाल करें. ध्यान दें कि डिवाइस को घुमाने पर, स्क्रीन खाली हो जाती है. ऐसा इसलिए होता है, क्योंकि ड्रॉइंग की स्थिति सेव नहीं की जाती है. इस सैंपल ऐप्लिकेशन के लिए, यह सुविधा डिज़ाइन के हिसाब से काम करती है. इससे उपयोगकर्ता को स्क्रीन को आसानी से साफ़ करने का विकल्प मिलता है.

पांचवां चरण: स्केच के चारों ओर एक फ़्रेम बनाएं

जब उपयोगकर्ता स्क्रीन पर ड्रॉ करता है, तो आपका ऐप्लिकेशन पाथ बनाता है और उसे बिटमैप extraBitmap में सेव करता है. onDraw() तरीके से, व्यू के कैनवस में अतिरिक्त बिटमैप दिखता है. onDraw() में ज़्यादा ड्राइंग की जा सकती है. उदाहरण के लिए, बिटमैप बनाने के बाद, शेप बनाए जा सकते हैं.

इस चरण में, आपको फ़ोटो के किनारे पर एक फ़्रेम बनाना होता है.

  1. MyCanvasView में, frame नाम का एक वैरिएबल जोड़ें. यह Rect ऑब्जेक्ट को सेव करता है.
private lateinit var frame: Rect
  1. आखिर में, onSizeChanged() एक इंसर्ट तय करें. साथ ही, नए डाइमेंशन और इंसर्ट का इस्तेमाल करके, फ़्रेम के लिए इस्तेमाल किया जाने वाला Rect बनाने के लिए कोड जोड़ें.
// Calculate a rectangular frame around the picture.
val inset = 40
frame = Rect(inset, inset, width - inset, height - inset)
  1. onDraw() में, बिटमैप बनाने के बाद, एक रेक्टैंगल बनाएं.
// Draw a frame around the canvas.
canvas.drawRect(frame, paint)
  1. अपना ऐप्लिकेशन चलाएं. फ़्रेम पर ध्यान दें.

टास्क (ज़रूरी नहीं): डेटा को पाथ में सेव करना

मौजूदा ऐप्लिकेशन में, ड्राइंग की जानकारी को बिटमैप में सेव किया जाता है. यह एक अच्छा समाधान है. हालांकि, ड्राइंग की जानकारी सेव करने का यह एकमात्र तरीका नहीं है. ड्राइंग के इतिहास को सेव करने का तरीका, ऐप्लिकेशन और आपकी अलग-अलग ज़रूरतों पर निर्भर करता है. उदाहरण के लिए, अगर आपको शेप बनाने हैं, तो उनकी जगह और डाइमेंशन की जानकारी के साथ शेप की सूची सेव की जा सकती है. MiniPaint ऐप्लिकेशन के लिए, पाथ को Path के तौर पर सेव किया जा सकता है. अगर आपको यह सुविधा आज़मानी है, तो यहां इसे इस्तेमाल करने का तरीका बताया गया है.

  1. MyCanvasView में, extraCanvas और extraBitmap के लिए इस्तेमाल किया गया सारा कोड हटाएं.
  2. अब तक के पाथ और अभी बनाए जा रहे पाथ के लिए वैरिएबल जोड़ें.
// Path representing the drawing so far
private val drawing = Path()

// Path representing what's currently being drawn
private val curPath = Path()
  1. onDraw() में, बिटमैप बनाने के बजाय, सेव किए गए और मौजूदा पाथ बनाएं.
// Draw the drawing so far
canvas.drawPath(drawing, paint)
// Draw any current squiggle
canvas.drawPath(curPath, paint)
// Draw a frame around the canvas
canvas.drawRect(frame, paint)
  1. touchUp() में, मौजूदा पाथ को पिछले पाथ में जोड़ें और मौजूदा पाथ को रीसेट करें.
// Add the current path to the drawing so far
drawing.addPath(curPath)
// Rewind the current path for the next touch
curPath.reset()
  1. ऐप्लिकेशन चलाएं. हां, इसमें कोई अंतर नहीं होना चाहिए.

पूरे किए गए कोडलैब का कोड डाउनलोड करें..

$  git clone https://github.com/googlecodelabs/android-kotlin-drawing-canvas


इसके अलावा, रिपॉज़िटरी को Zip फ़ाइल के तौर पर डाउनलोड किया जा सकता है. इसके बाद, इसे अनज़िप करके Android Studio में खोला जा सकता है.

ज़िप फ़ाइल डाउनलोड करें

  • Canvas, ड्रॉइंग के लिए 2D जगह होती है. इसमें ड्रॉइंग के तरीके उपलब्ध होते हैं.
  • Canvas को View के उस इंस्टेंस से जोड़ा जा सकता है जो इसे दिखाता है.
  • Paint ऑब्जेक्ट में, ज्यामिति (जैसे कि लाइन, आयत, अंडाकार, और पाथ) और टेक्स्ट को ड्रा करने के तरीके के बारे में स्टाइल और रंग की जानकारी होती है.
  • कैनवस के साथ काम करने का एक सामान्य पैटर्न यह है कि एक कस्टम व्यू बनाया जाए और onDraw() और onSizeChanged() तरीकों को बदल दिया जाए.
  • उपयोगकर्ता के टच को कैप्चर करने और चीज़ें बनाकर उनका जवाब देने के लिए, onTouchEvent() तरीके को बदलें.
  • समय के साथ बदलने वाली ड्रॉइंग के लिए, जानकारी को कैश मेमोरी में सेव करने के लिए किसी अतिरिक्त बिटमैप का इस्तेमाल किया जा सकता है. इसके अलावा, आकृतियों या पाथ को सेव किया जा सकता है.

Udacity का कोर्स:

Android डेवलपर का दस्तावेज़:

इस सेक्शन में, उन छात्र-छात्राओं के लिए होमवर्क असाइनमेंट की सूची दी गई है जो किसी शिक्षक के कोर्स के हिस्से के तौर पर इस कोडलैब पर काम कर रहे हैं. शिक्षक के पास ये विकल्प होते हैं:

  • अगर ज़रूरी हो, तो होमवर्क असाइन करें.
  • छात्र-छात्राओं को बताएं कि होमवर्क असाइनमेंट कैसे सबमिट किए जाते हैं.
  • होमवर्क असाइनमेंट को ग्रेड दें.

शिक्षक इन सुझावों का इस्तेमाल अपनी ज़रूरत के हिसाब से कर सकते हैं. साथ ही, वे चाहें, तो कोई दूसरा होमवर्क भी दे सकते हैं.

अगर आपको यह कोडलैब खुद से पूरा करना है, तो अपनी जानकारी की जांच करने के लिए, इन होमवर्क असाइनमेंट का इस्तेमाल करें.

इन सवालों के जवाब दें

पहला सवाल

Canvas के साथ काम करने के लिए, इनमें से कौनसे कॉम्पोनेंट ज़रूरी हैं? लागू होने वाले सभी विकल्पों को चुनें.

Bitmap

Paint

Path

View

दूसरा सवाल

invalidate() को कॉल करने से क्या होता है (सामान्य शब्दों में)?

▢ इससे आपका ऐप्लिकेशन बंद हो जाता है और फिर से शुरू होता है.

▢ इससे बिटमैप से ड्रॉइंग मिट जाती है.

▢ इससे पता चलता है कि पिछले कोड को नहीं चलाना चाहिए.

▢ यह सिस्टम को बताता है कि उसे स्क्रीन को फिर से रेंडर करना है.

तीसरा सवाल

Canvas, Bitmap, और Paint ऑब्जेक्ट का क्या काम है?

▢ 2D ड्राइंग का सरफ़ेस, स्क्रीन पर दिखाया गया बिटमैप, और ड्राइंग के स्टाइल की जानकारी.

▢ 3D ड्रॉइंग सर्फ़ेस, पाथ को कैश मेमोरी में सेव करने के लिए बिटमैप, ड्रॉइंग के लिए स्टाइलिंग की जानकारी.

▢ 2D ड्रॉइंग सर्फ़ेस, स्क्रीन पर दिखने वाला बिटमैप, व्यू के लिए स्टाइलिंग.

▢ ड्राइंग की जानकारी के लिए कैश मेमोरी, ड्राइंग के लिए बिटमैप, ड्राइंग के लिए स्टाइलिंग की जानकारी.

इस कोर्स में मौजूद अन्य कोडलैब के लिंक के लिए, Advanced Android in Kotlin कोडलैब का लैंडिंग पेज देखें.