कम्पोज़ में ग्राफ़िक्स

कई ऐप्लिकेशन को स्क्रीन पर सटीक तरीके से ड्रॉ करने की ज़रूरत होती है. यह काम, स्क्रीन पर सही जगह पर कोई बॉक्स या सर्कल बनाने जितना आसान हो सकता है. इसके अलावा, यह कई अलग-अलग स्टाइल में ग्राफ़िक एलिमेंट की जटिल व्यवस्था भी हो सकती है.

मॉडिफ़ायर और DrawScope की मदद से बुनियादी ड्रॉइंग करना

Compose में, अपनी पसंद के मुताबिक कुछ ड्रॉ करने का मुख्य तरीका, मॉडिफ़ायर का इस्तेमाल करना है. जैसे, Modifier.drawWithContent, Modifier.drawBehind, और Modifier.drawWithCache.

उदाहरण के लिए, अपने कंपोज़ेबल के पीछे कुछ ड्रॉ करने के लिए, ड्रॉइंग के निर्देश लागू करने के लिए drawBehind मॉडिफ़ायर का इस्तेमाल किया जा सकता है:

Spacer(
    modifier = Modifier
        .fillMaxSize()
        .drawBehind {
            // this = DrawScope
        }
)

अगर आपको सिर्फ़ ऐसा कंपोज़ेबल चाहिए जो ड्रॉ करता हो, तो Canvas कंपोज़ेबल का इस्तेमाल किया जा सकता है. Canvas कंपोज़ेबल, Modifier.drawBehind के लिए रैपर का काम करता है. Canvas को अपने लेआउट में उसी तरह रखा जाता है जिस तरह Compose के किसी अन्य यूज़र इंटरफ़ेस (यूआई) एलिमेंट को रखा जाता है. Canvas में, एलिमेंट को उनकी स्टाइल और जगह पर सटीक तरीके से कंट्रोल करके ड्रॉ किया जा सकता है.

ड्रॉइंग के सभी मॉडिफ़ायर, DrawScope को दिखाते हैं. यह स्कोप किया गया ड्रॉइंग एनवायरमेंट है, जो अपनी स्थिति बनाए रखता है. इससे, ग्राफ़िकल एलिमेंट के ग्रुप के लिए पैरामीटर सेट किए जा सकते हैं. DrawScope कई काम के फ़ील्ड उपलब्ध कराता है. जैसे, size. यह Size ऑब्जेक्ट है, जो DrawScope के मौजूदा डाइमेंशन के बारे में बताता है.

कुछ ड्रॉ करने के लिए, DrawScope पर मौजूद ड्रॉइंग के कई फ़ंक्शन में से किसी एक का इस्तेमाल किया जा सकता है. उदाहरण के लिए, यहां दिया गया कोड, स्क्रीन के सबसे ऊपर बाएं कोने में एक आयत ड्रॉ करता है:

Canvas(modifier = Modifier.fillMaxSize()) {
    val canvasQuadrantSize = size / 2F
    drawRect(
        color = Color.Magenta,
        size = canvasQuadrantSize
    )
}

सफ़ेद बैकग्राउंड पर गुलाबी रंग का आयत बनाया गया है, जो स्क्रीन के एक चौथाई हिस्से पर है
पहली इमेज. Compose में कैनवस का इस्तेमाल करके ड्रॉ की गई आयत.

ड्रॉइंग के अलग-अलग मॉडिफ़ायर के बारे में ज़्यादा जानने के लिए, ग्राफ़िक्स मॉडिफ़ायर का दस्तावेज़ देखें.

कोऑर्डिनेट सिस्टम

स्क्रीन पर कुछ ड्रॉ करने के लिए, आपको अपने आइटम के ऑफ़सेट (x और y) और साइज़ के बारे में पता होना चाहिए. DrawScope पर मौजूद ड्रॉइंग के कई तरीकों में, पोज़िशन और साइज़ डिफ़ॉल्ट पैरामीटर वैल्यू से तय होते हैं. डिफ़ॉल्ट पैरामीटर आम तौर पर, आइटम को कैनवस पर [0, 0] पॉइंट पर रखते हैं. साथ ही, डिफ़ॉल्ट size उपलब्ध कराते हैं, जो ऊपर दिए गए उदाहरण की तरह, पूरे ड्रॉइंग एरिया को भर देता है. इसमें देखा जा सकता है कि आयत, सबसे ऊपर बाएं कोने में है. अपने आइटम के साइज़ और पोज़िशन को अडजस्ट करने के लिए, आपको Compose में कोऑर्डिनेट सिस्टम को समझना होगा.

कोऑर्डिनेट सिस्टम का शुरुआती पॉइंट ([0,0]), ड्रॉइंग एरिया में सबसे ऊपर बाएं कोने वाला पिक्सल होता है. दाईं ओर जाने पर x बढ़ता है और नीचे की ओर जाने पर y बढ़ता है.

एक ग्रिड में कोऑर्डिनेट सिस्टम दिखाया गया है. इसमें सबसे ऊपर बाएं कोने में [0, 0] और सबसे नीचे दाएं कोने में [चौड़ाई, ऊंचाई] दिखाया गया है
दूसरी इमेज. ड्रॉइंग कोऑर्डिनेट सिस्टम / ड्रॉइंग ग्रिड.

उदाहरण के लिए, अगर आपको कैनवस एरिया के सबसे ऊपर दाएं कोने से सबसे नीचे बाएं कोने तक, डायगनल लाइन ड्रॉ करनी है, तो DrawScope.drawLine() फ़ंक्शन का इस्तेमाल किया जा सकता है. साथ ही, x और y की पोज़िशन के हिसाब से, शुरुआती और आखिरी ऑफ़सेट तय किया जा सकता है:

Canvas(modifier = Modifier.fillMaxSize()) {
    val canvasWidth = size.width
    val canvasHeight = size.height
    drawLine(
        start = Offset(x = canvasWidth, y = 0f),
        end = Offset(x = 0f, y = canvasHeight),
        color = Color.Blue
    )
}

बुनियादी बदलाव

DrawScope , बदलाव करने की सुविधा देता है. इससे यह तय किया जा सकता है कि ड्रॉइंग के निर्देश कहां या कैसे लागू किए जाएं.

स्केल

अपनी ड्रॉइंग की कार्रवाइयों का साइज़ बढ़ाने के लिए, DrawScope.scale() का इस्तेमाल करें. scale() जैसी कार्रवाइयां, इससे जुड़े लैम्डा में ड्रॉइंग की सभी कार्रवाइयों पर लागू होती हैं. उदाहरण के लिए, यहां दिया गया कोड, scaleX को 10 गुना और scaleY को 15 गुना बढ़ाता है:

Canvas(modifier = Modifier.fillMaxSize()) {
    scale(scaleX = 10f, scaleY = 15f) {
        drawCircle(Color.Blue, radius = 20.dp.toPx())
    }
}

सर्कल को अलग-अलग डाइमेंशन में स्केल किया गया है
तीसरी इमेज. कैनवस पर मौजूद सर्कल पर स्केल की कार्रवाई लागू करना.

ट्रांसलेट

अपनी ड्रॉइंग की कार्रवाइयों को ऊपर, नीचे, बाएं या दाएं ले जाने के लिए, DrawScope.translate() का इस्तेमाल करें. उदाहरण के लिए, यहां दिया गया कोड, ड्रॉइंग को 100 पिक्सल दाईं ओर और 300 पिक्सल ऊपर ले जाता है:

Canvas(modifier = Modifier.fillMaxSize()) {
    translate(left = 100f, top = -300f) {
        drawCircle(Color.Blue, radius = 200.dp.toPx())
    }
}

ऐसा सर्कल जो सेंटर से हट गया हो
चौथी इमेज. कैनवस पर मौजूद सर्कल पर ट्रांसलेट की कार्रवाई लागू करना.

रोटेट

अपनी ड्रॉइंग की कार्रवाइयों को पिवट पॉइंट के आस-पास घुमाने के लिए, DrawScope.rotate() का इस्तेमाल करें. उदाहरण के लिए, यहां दिया गया कोड, आयत को 45 डिग्री पर घुमाता है:

Canvas(modifier = Modifier.fillMaxSize()) {
    rotate(degrees = 45F) {
        drawRect(
            color = Color.Gray,
            topLeft = Offset(x = size.width / 3F, y = size.height / 3F),
            size = size / 3F
        )
    }
}

एक फ़ोन, जिसकी स्क्रीन के बीच में 45 डिग्री पर घुमाया गया आयत दिख रहा है
पांचवीं इमेज. हम मौजूदा ड्रॉइंग स्कोप पर रोटेशन लागू करने के लिए, rotate() का इस्तेमाल करते हैं. इससे आयत 45 डिग्री पर घूम जाती है.

इनसेट

मौजूदा DrawScope के डिफ़ॉल्ट पैरामीटर को अडजस्ट करने के लिए, DrawScope.inset() का इस्तेमाल करें. इससे ड्रॉइंग की बाउंड्री बदल जाती हैं और ड्रॉइंग को उसके हिसाब से ट्रांसलेट किया जाता है:

Canvas(modifier = Modifier.fillMaxSize()) {
    val canvasQuadrantSize = size / 2F
    inset(horizontal = 50f, vertical = 30f) {
        drawRect(color = Color.Green, size = canvasQuadrantSize)
    }
}

इस कोड से, ड्रॉइंग के निर्देशों में पैडिंग जुड़ जाती है:

ऐसा आयत जिसके चारों ओर पैडिंग की गई है
छठी इमेज. ड्रॉइंग के निर्देशों पर इनसेट लागू करना.

कई बदलाव

अपनी ड्रॉइंग पर कई बदलाव लागू करने के लिए, DrawScope.withTransform() फ़ंक्शन का इस्तेमाल करें. इससे एक ऐसा बदलाव बनता है और लागू होता है जिसमें आपके सभी ज़रूरी बदलाव शामिल होते हैं. withTransform() का इस्तेमाल करना, अलग-अलग बदलावों के लिए नेस्टेड कॉल करने से ज़्यादा कारगर है. ऐसा इसलिए, क्योंकि सभी बदलाव एक साथ एक ही कार्रवाई में किए जाते हैं. इसके बजाय, Compose को नेस्टेड बदलावों में से हर एक को कैलकुलेट और सेव नहीं करना पड़ता.

उदाहरण के लिए, यहां दिया गया कोड, आयत पर ट्रांसलेशन और रोटेशन, दोनों लागू करता है:

Canvas(modifier = Modifier.fillMaxSize()) {
    withTransform({
        translate(left = size.width / 5F)
        rotate(degrees = 45F)
    }) {
        drawRect(
            color = Color.Gray,
            topLeft = Offset(x = size.width / 3F, y = size.height / 3F),
            size = size / 3F
        )
    }
}

एक फ़ोन, जिसमें घुमाया गया आयत स्क्रीन के किनारे पर दिख रहा है
सातवीं इमेज. रोटेशन और ट्रांसलेशन, दोनों लागू करने के लिए withTransform का इस्तेमाल करें. इससे आयत घूम जाती है और बाईं ओर खिसक जाती है.

ड्रॉइंग की सामान्य कार्रवाइयां

टेक्स्ट ड्रॉ करना

Compose में टेक्स्ट ड्रॉ करने के लिए, आम तौर पर Text कंपोज़ेबल का इस्तेमाल किया जाता है. हालांकि, अगर आप DrawScope में हैं या अपनी पसंद के मुताबिक बदलाव करके, टेक्स्ट को मैन्युअल तरीके से ड्रॉ करना चाहते हैं, तो DrawScope.drawText() तरीके का इस्तेमाल किया जा सकता है.

टेक्स्ट ड्रॉ करने के लिए, TextMeasurer का इस्तेमाल करके rememberTextMeasurer बनाएं और मेज़रर के साथ drawText को कॉल करें:

val textMeasurer = rememberTextMeasurer()

Canvas(modifier = Modifier.fillMaxSize()) {
    drawText(textMeasurer, "Hello")
}

कैनवस पर बनाई गई Hello की ड्रॉइंग दिखाने वाली इमेज
आठवीं इमेज. कैनवस पर टेक्स्ट ड्रॉ करना.

टेक्स्ट का साइज़ मेज़र करना

टेक्स्ट ड्रॉ करने का तरीका, ड्रॉइंग के अन्य निर्देशों से थोड़ा अलग होता है. आम तौर पर, ड्रॉइंग के निर्देश में साइज़ (चौड़ाई और ऊंचाई) दिया जाता है, ताकि शेप/इमेज को उस साइज़ में ड्रॉ किया जा सके. टेक्स्ट के मामले में, कुछ पैरामीटर होते हैं जो रेंडर किए गए टेक्स्ट के साइज़ को कंट्रोल करते हैं. जैसे, फ़ॉन्ट साइज़, फ़ॉन्ट, लिगेचर, और लेटर स्पेसिंग.

Compose में, TextMeasurer का इस्तेमाल करके, टेक्स्ट के मेज़र किए गए साइज़ का ऐक्सेस पाया जा सकता है. यह साइज़, ऊपर बताए गए फ़ैक्टर पर निर्भर करता है. अगर टेक्स्ट के पीछे बैकग्राउंड ड्रॉ करना है, तो मेज़र की गई जानकारी का इस्तेमाल करके, उस एरिया का साइज़ पाया जा सकता है जिसमें टेक्स्ट मौजूद है:

val textMeasurer = rememberTextMeasurer()

Spacer(
    modifier = Modifier
        .drawWithCache {
            val measuredText =
                textMeasurer.measure(
                    AnnotatedString(longTextSample),
                    constraints = Constraints.fixedWidth((size.width * 2f / 3f).toInt()),
                    style = TextStyle(fontSize = 18.sp)
                )

            onDrawBehind {
                drawRect(pinkColor, size = measuredText.size.toSize())
                drawText(measuredText)
            }
        }
        .fillMaxSize()
)

इस कोड स्निपेट से, टेक्स्ट का बैकग्राउंड गुलाबी हो जाता है:

पूरी जगह के दो-तिहाई हिस्से में कई लाइनों वाला टेक्स्ट, जिसके पीछे आयताकार बैकग्राउंड है
नौवीं इमेज. मल्टी-लाइन टेक्स्ट, पूरे एरिया के ⅔ साइज़ में है. साथ ही, इसका बैकग्राउंड आयताकार है.

बाउंड्री, फ़ॉन्ट साइज़ या मेज़र किए गए साइज़ पर असर डालने वाली किसी भी प्रॉपर्टी को अडजस्ट करने पर, नया साइज़ रिपोर्ट किया जाता है. width और height दोनों के लिए फ़िक्स्ड साइज़ सेट किया जा सकता है. इसके बाद, टेक्स्ट सेट किए गए TextOverflow के हिसाब से दिखता है. उदाहरण के लिए, यहां दिया गया कोड, कंपोज़ेबल एरिया की ऊंचाई के ⅓ और चौड़ाई के ⅓ में टेक्स्ट रेंडर करता है. साथ ही, TextOverflow को TextOverflow.Ellipsis पर सेट करता है:

val textMeasurer = rememberTextMeasurer()

Spacer(
    modifier = Modifier
        .drawWithCache {
            val measuredText =
                textMeasurer.measure(
                    AnnotatedString(longTextSample),
                    constraints = Constraints.fixed(
                        width = (size.width / 3f).toInt(),
                        height = (size.height / 3f).toInt()
                    ),
                    overflow = TextOverflow.Ellipsis,
                    style = TextStyle(fontSize = 18.sp)
                )

            onDrawBehind {
                drawRect(pinkColor, size = measuredText.size.toSize())
                drawText(measuredText)
            }
        }
        .fillMaxSize()
)

अब टेक्स्ट, बाउंड्री में ड्रॉ किया गया है. इसके आखिर में एलिप्सिस है:

गुलाबी बैकग्राउंड पर टेक्स्ट लिखा गया है. टेक्स्ट को कट ऑफ़ करने के लिए, तीन बिंदुओं का इस्तेमाल किया गया है.
10वीं इमेज. टेक्स्ट का साइज़ मेज़र करने के लिए, फ़िक्स्ड बाउंड्री के साथ TextOverflow.Ellipsis का इस्तेमाल करना.

इमेज ड्रॉ करना

DrawScope की मदद से ImageBitmap ड्रॉ करने के लिए, ImageBitmap.imageResource() का इस्तेमाल करके इमेज लोड करें. इसके बाद, drawImage को कॉल करें:

val dogImage = ImageBitmap.imageResource(id = R.drawable.dog)

Canvas(modifier = Modifier.fillMaxSize(), onDraw = {
    drawImage(dogImage)
})

Canvas पर कुत्ते की बनाई गई इमेज
11वीं इमेज. कैनवस पर ImageBitmap ड्रॉ करना.

बुनियादी शेप ड्रॉ करना

DrawScope पर, शेप ड्रॉ करने के कई फ़ंक्शन मौजूद हैं. कोई शेप ड्रॉ करने के लिए, पहले से तय किए गए ड्रॉइंग के किसी फ़ंक्शन का इस्तेमाल करें. जैसे, drawCircle:

val purpleColor = Color(0xFFBA68C8)
Canvas(
    modifier = Modifier
        .fillMaxSize()
        .padding(16.dp),
    onDraw = {
        drawCircle(purpleColor)
    }
)

एपीआई

आउटपुट

drawCircle()

सर्कल बनाओ

drawRect()

draw rect

drawRoundedRect()

draw rounded rect

drawLine()

लाइन बनाना

drawOval()

अंडाकार बनाना

drawArc()

आर्क बनाएं

drawPoints()

ड्रॉ पॉइंट

पाथ ड्रॉ करना

पाथ, गणित के निर्देशों की एक सीरीज़ होती है. इन्हें लागू करने पर, ड्रॉइंग मिलती है. DrawScope, DrawScope.drawPath() तरीके का इस्तेमाल करके पाथ ड्रॉ कर सकता है.

उदाहरण के लिए, मान लें कि आपको कोई त्रिकोण ड्रॉ करना है. ड्रॉइंग एरिया के साइज़ का इस्तेमाल करके, lineTo() और moveTo() जैसे फ़ंक्शन की मदद से पाथ जनरेट किया जा सकता है. इसके बाद, त्रिकोण पाने के लिए, इस नए पाथ के साथ drawPath() को कॉल करें.

Spacer(
    modifier = Modifier
        .drawWithCache {
            val path = Path()
            path.moveTo(0f, 0f)
            path.lineTo(size.width / 2f, size.height / 2f)
            path.lineTo(size.width, 0f)
            path.close()
            onDrawBehind {
                drawPath(path, Color.Magenta, style = Stroke(width = 10f))
            }
        }
        .fillMaxSize()
)

Compose पर उल्टा करके बनाया गया बैंगनी रंग का पाथ ट्राएंगल
12वीं इमेज. Compose में Path बनाना और ड्रॉ करना.

Canvas ऑब्जेक्ट को ऐक्सेस करना

DrawScope की मदद से, Canvas ऑब्जेक्ट को सीधे ऐक्सेस नहीं किया जा सकता. Canvas ऑब्जेक्ट को ऐक्सेस करने के लिए, DrawScope.drawIntoCanvas() का इस्तेमाल किया जा सकता है. इस ऑब्जेक्ट पर फ़ंक्शन कॉल किए जा सकते हैं.

उदाहरण के लिए, अगर आपके पास कोई कस्टम Drawable है जिसे कैनवस पर ड्रॉ करना है, तो कैनवस को ऐक्सेस किया जा सकता है और Drawable#draw() को कॉल किया जा सकता है. इसके लिए, Canvas ऑब्जेक्ट पास करें:

val drawable = ShapeDrawable(OvalShape())
Spacer(
    modifier = Modifier
        .drawWithContent {
            drawIntoCanvas { canvas ->
                drawable.setBounds(0, 0, size.width.toInt(), size.height.toInt())
                drawable.draw(canvas.nativeCanvas)
            }
        }
        .fillMaxSize()
)

फ़ुल साइज़ का अंडाकार काला ShapeDrawable
13वीं इमेज. Drawable ड्रॉ करने के लिए, कैनवस को ऐक्सेस करना.

ज़्यादा जानें

Compose में ड्रॉइंग के बारे में ज़्यादा जानकारी पाने के लिए, ये संसाधन देखें: