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

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

मॉडिफ़ायर और 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())
    }
}

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

अनुवाद करें

अपनी ड्राइंग को ऊपर, नीचे, बाएं या दाएं ले जाने के लिए, 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 के डिफ़ॉल्ट पैरामीटर में बदलाव करने के लिए, 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() तरीके का इस्तेमाल किया जा सकता है.

टेक्स्ट बनाने के लिए, 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()
)

पूरे साइज़ में एक अंडाकार ब्लैक शेपड्रॉएबल
13वीं इमेज. Drawable बनाने के लिए कैनवस को ऐक्सेस करना.

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

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