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

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

मॉडिफ़ायर और 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 में Canvas का इस्तेमाल करके बनाया गया आयत.

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

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

स्क्रीन पर कुछ भी बनाने के लिए, आपको अपने आइटम के ऑफ़सेट (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())
    }
}

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

अनुवाद करें

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

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

ऐसा सर्कल जो सेंटर से हट गया हो
चौथी इमेज. Canvas पर मौजूद किसी सर्कल पर अनुवाद करने की सुविधा का इस्तेमाल करना.

घुमाएं

किसी पिवट पॉइंट के चारों ओर ड्रॉइंग को घुमाने के लिए, 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.inset() का इस्तेमाल करके, मौजूदा DrawScope के डिफ़ॉल्ट पैरामीटर में बदलाव करें. इससे ड्रॉइंग की सीमाएं बदल जाएंगी और ड्रॉइंग का अनुवाद भी उसी के हिसाब से हो जाएगा:

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() तरीके का इस्तेमाल किया जा सकता है.

टेक्स्ट बनाने के लिए, rememberTextMeasurer का इस्तेमाल करके TextMeasurer बनाएं और मेज़रर के साथ 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()
)

टेक्स्ट को अब इन शर्तों के साथ दिखाया गया है. इसके आखिर में एलिप्सिस है:

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

इमेज ड्रॉ करें

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

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

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

Canvas पर कुत्ते की बनाई गई इमेज
11वीं इमेज. Canvas पर 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 है और आपको उसे कैनवस पर ड्रा करना है, तो कैनवस को ऐक्सेस किया जा सकता है. इसके बाद, Canvas ऑब्जेक्ट को पास करके Drawable#draw() को कॉल किया जा सकता है:

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 में ड्रॉइंग के बारे में ज़्यादा जानने के लिए, यहां दिए गए संसाधन देखें: