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

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

मॉडिफ़ायर और 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 का मौजूदा डाइमेंशन बताने वाला 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 composable का इस्तेमाल किया जा सकता है. हालांकि, अगर आप DrawScope में हैं या आपको अपनी पसंद के मुताबिक टेक्स्ट मैन्युअल तरीके से बनाना है, तो DrawScope.drawText() वाला तरीका अपनाएं.

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

val textMeasurer = rememberTextMeasurer()

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

कैनवस पर 'नमस्ते' लिखा हुआ दिखाया गया है
आठवीं इमेज. कैनवस पर टेक्स्ट ड्रॉ करना.

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

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

ऊपर बताए गए फ़ैक्टर के आधार पर, 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)
})

कैनवस पर बनाए गए कुत्ते की इमेज
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 है और आपको उसे कैनवस पर बनाना है, तो कैनवस को ऐक्सेस करें और 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 में ड्रॉ करने के बारे में ज़्यादा जानने के लिए, यहां दिए गए संसाधन देखें: