Compose में Canvas
कॉम्पोज़ेबल के अलावा, कई काम के ग्राफ़िक भी मौजूद हैंModifiers
. इनकी मदद से, कस्टम कॉन्टेंट बनाया जा सकता है. ये मॉडिफ़ायर इसलिए कारगर होते हैं,
क्योंकि इन्हें किसी भी कंपोज़ेबल पर लागू किया जा सकता है.
ड्रॉइंग मॉडिफ़ायर
सभी ड्रॉइंग कमांड, Compose में ड्रॉइंग मॉडिफ़ायर की मदद से पूरे किए जाते हैं. Compose में, ड्रॉइंग में बदलाव करने के लिए तीन मुख्य विकल्प होते हैं:
ड्रॉइंग के लिए बुनियादी मॉडिफ़ायर drawWithContent
है. यहां आपके पास अपने Composable के ड्रॉइंग क्रम और मॉडिफ़ायर में दिए गए ड्रॉइंग निर्देशों को तय करने का विकल्प होता है. drawBehind
एक ऐसा रैपर है जो drawWithContent
की स्क्रीन पर आसानी से इस्तेमाल किया जा सकता है.
इसमें ड्रॉइंग का क्रम, कंपोज़ेबल के कॉन्टेंट के पीछे सेट होता है. drawWithCache
, अपने अंदर onDrawBehind
या onDrawWithContent
को कॉल करता है. साथ ही, इनमें बनाए गए ऑब्जेक्ट को कैश मेमोरी में सेव करने का तरीका भी उपलब्ध कराता है.
Modifier.drawWithContent
: ड्रॉइंग का क्रम चुनना
Modifier.drawWithContent
की मदद से, कॉम्पोज़ेबल के कॉन्टेंट से पहले या बाद में, DrawScope
ऑपरेशन किए जा सकते हैं. इसके बाद, कंपोज़ेबल का असल कॉन्टेंट रेंडर करने के लिए drawContent
को कॉल करना न भूलें. अगर आप कॉन्टेंट को अपनी पसंद के मुताबिक बनाई जाने वाली ड्रॉइंग से पहले या बाद में ड्रॉ करना चाहते हैं, तो इस मॉडिफ़ायर का इस्तेमाल करके कार्रवाइयों का क्रम तय किया जा सकता है.
उदाहरण के लिए, अगर आपको अपने कॉन्टेंट के ऊपर रेडियल ग्रेडिएंट रेंडर करना है, ताकि यूज़र इंटरफ़ेस (यूआई) पर फ़्लैशलाइट की-होल इफ़ेक्ट बनाया जा सके, तो यह तरीका अपनाएं:
var pointerOffset by remember { mutableStateOf(Offset(0f, 0f)) } Column( modifier = Modifier .fillMaxSize() .pointerInput("dragging") { detectDragGestures { change, dragAmount -> pointerOffset += dragAmount } } .onSizeChanged { pointerOffset = Offset(it.width / 2f, it.height / 2f) } .drawWithContent { drawContent() // draws a fully black area with a small keyhole at pointerOffset that’ll show part of the UI. drawRect( Brush.radialGradient( listOf(Color.Transparent, Color.Black), center = pointerOffset, radius = 100.dp.toPx(), ) ) } ) { // Your composables here }
Modifier.drawBehind
: किसी कॉम्पोज़ेबल के पीछे ड्रॉ करना
Modifier.drawBehind
की मदद से, स्क्रीन पर ड्रॉ किए गए कंपोज़ेबल कॉन्टेंट के साथ DrawScope
कार्रवाइयां की जा सकती हैं. Canvas
को लागू करने के तरीके को देखकर, आपको पता चल सकता है कि यह Modifier.drawBehind
के लिए सिर्फ़ एक सुविधाजनक रैपर है.
Text
के पीछे गोलाकार कोनों वाला रेक्टैंगल बनाने के लिए:
Text( "Hello Compose!", modifier = Modifier .drawBehind { drawRoundRect( Color(0xFFBBAAEE), cornerRadius = CornerRadius(10.dp.toPx()) ) } .padding(4.dp) )
इससे यह नतीजा मिलता है:
Modifier.drawWithCache
: ड्रॉ ऑब्जेक्ट को ड्रॉ करना और कैश मेमोरी में सेव करना
Modifier.drawWithCache
, अपने अंदर बनाए गए ऑब्जेक्ट को कैश मेमोरी में सेव रखता है. ऑब्जेक्ट तब तक कैश मेमोरी में सेव किए जाते हैं, जब तक ड्रॉइंग एरिया का साइज़ एक जैसा हो या पढ़े गए स्टेट ऑब्जेक्ट में कोई बदलाव न हुआ हो. यह मॉडिफ़ायर, ड्रॉइंग कॉल की परफ़ॉर्मेंस को बेहतर बनाने के लिए मददगार है. ऐसा इसलिए, क्योंकि इससे ड्रॉ पर बनाए गए ऑब्जेक्ट (जैसे: Brush, Shader, Path
वगैरह) को फिर से असाइन करने की ज़रूरत नहीं पड़ती.
इसके अलावा, ऑब्जेक्ट को कैश मेमोरी में सेव करने के लिए, remember
का इस्तेमाल भी किया जा सकता है. हालांकि, हमेशा ऐसा करना मुमकिन नहीं होता, क्योंकि आपके पास कंपोज़िशन का ऐक्सेस हमेशा नहीं होता. अगर ऑब्जेक्ट का इस्तेमाल सिर्फ़ ड्रॉइंग के लिए किया जाता है, तो drawWithCache
का इस्तेमाल करने पर बेहतर परफ़ॉर्मेंस मिल सकती है.
उदाहरण के लिए, अगर Text
के पीछे ग्रेडिएंट बनाने के लिए Brush
बनाया जाता है, तो drawWithCache
का इस्तेमाल करने पर Brush
ऑब्जेक्ट को तब तक कैश मेमोरी में सेव रखा जाता है, जब तक कि ड्रॉइंग एरिया का साइज़ नहीं बदलता:
Text( "Hello Compose!", modifier = Modifier .drawWithCache { val brush = Brush.linearGradient( listOf( Color(0xFF9E82F0), Color(0xFF42A5F5) ) ) onDrawBehind { drawRoundRect( brush, cornerRadius = CornerRadius(10.dp.toPx()) ) } } )
ग्राफ़िक मॉडिफ़ायर
Modifier.graphicsLayer
: कॉम्पोज़ेबल पर ट्रांसफ़ॉर्मेशन लागू करना
Modifier.graphicsLayer
एक मॉडिफ़ायर है, जो कॉम्पोज़ेबल ड्रॉ के कॉन्टेंट को ड्रॉ लेयर में बदल देता है. लेयर की मदद से, कई काम किए जा सकते हैं. जैसे:
- ड्रॉइंग करने से जुड़े निर्देशों के लिए अलग-अलग (
RenderNode
से मिलता-जुलता). लेयर के हिस्से के तौर पर कैप्चर किए गए ड्रॉइंग निर्देशों को, ऐप्लिकेशन कोड को फिर से लागू किए बिना, रेंडर करने वाली पाइपलाइन के ज़रिए असरदार तरीके से फिर से जारी किया जा सकता है. - किसी लेयर में मौजूद ड्रॉइंग के सभी निर्देशों पर लागू होने वाले ट्रांसफ़ॉर्मेशन.
- कंपोज़िशन की सुविधाओं के लिए रेस्टराइज़ेशन. जब किसी लेयर को रेस्टर किया जाता है, तो उसके डिवाइस पर दिखाए जाने के निर्देश लागू होते हैं और आउटपुट को ऑफ़स्क्रीन बफ़र में कैप्चर किया जाता है. अगले फ़्रेम के लिए इस तरह के बफ़र को कंपोज़िट करना, अलग-अलग निर्देशों को लागू करने से ज़्यादा तेज़ होता है. हालांकि, स्केलिंग या रोटेशन जैसे ट्रांसफ़ॉर्म लागू होने पर, यह बिटमैप की तरह काम करेगा.
ट्रांसफ़ॉर्मेशन
Modifier.graphicsLayer
, ड्रॉइंग के निर्देशों के लिए अलगाव की सुविधा देता है. उदाहरण के लिए, Modifier.graphicsLayer
का इस्तेमाल करके अलग-अलग ट्रांसफ़ॉर्मेशन लागू किए जा सकते हैं.
इनमें ऐनिमेशन जोड़ा जा सकता है या इनमें बदलाव किया जा सकता है. इसके लिए, ड्रॉइंग के लिए इस्तेमाल किए गए लैम्ब्डा फ़ंक्शन को फिर से लागू करने की ज़रूरत नहीं होती.
Modifier.graphicsLayer
, आपके कंपोज़ेबल के मेज़र किए गए साइज़ या प्लेसमेंट में बदलाव नहीं करता, क्योंकि इससे सिर्फ़ ड्रॉ करने के चरण पर असर पड़ता है. इसका मतलब है कि अगर आपका कॉम्पोज़ेबल, अपने लेआउट के दायरे से बाहर ड्रॉ होता है, तो हो सकता है कि वह दूसरे कॉम्पोज़ेबल के ऊपर ओवरलैप हो जाए.
इस मॉडिफ़ायर का इस्तेमाल करके, यहां दिए गए बदलाव लागू किए जा सकते हैं:
स्केल - साइज़ बढ़ाना
scaleX
और scaleY
, कॉन्टेंट को हॉरिज़ॉन्टल या वर्टिकल दिशा में बड़ा या छोटा करते हैं. 1.0f
की वैल्यू से पता चलता है कि स्केल में कोई बदलाव नहीं हुआ है. 0.5f
की वैल्यू का मतलब है कि डाइमेंशन आधा हो गया है.
Image( painter = painterResource(id = R.drawable.sunset), contentDescription = "Sunset", modifier = Modifier .graphicsLayer { this.scaleX = 1.2f this.scaleY = 0.8f } )
अनुवाद
translationX
और translationY
को graphicsLayer
से बदला जा सकता है.
translationX
, कॉम्पोज़ेबल को बाईं या दाईं ओर ले जाता है. translationY
, कंपोज़ेबल को
ऊपर या नीचे ले जाता है.
Image( painter = painterResource(id = R.drawable.sunset), contentDescription = "Sunset", modifier = Modifier .graphicsLayer { this.translationX = 100.dp.toPx() this.translationY = 10.dp.toPx() } )
रोटेशन
हॉरिज़ॉन्टल तरीके से घुमाने के लिए rotationX
, वर्टिकल तरीके से घुमाने के लिए rotationY
, और Z ऐक्सिस (स्टैंडर्ड रोटेशन) पर घुमाने के लिए rotationZ
को सेट करें. यह वैल्यू डिग्री (0-360) में दी जाती है.
Image( painter = painterResource(id = R.drawable.sunset), contentDescription = "Sunset", modifier = Modifier .graphicsLayer { this.rotationX = 90f this.rotationY = 275f this.rotationZ = 180f } )
Origin
transformOrigin
तय किया जा सकता है. इसके बाद, इसका इस्तेमाल उस पॉइंट के तौर पर किया जाता है जहां ट्रांसफ़ॉर्मेशन होते हैं. अब तक दिए गए सभी उदाहरणों में TransformOrigin.Center
का इस्तेमाल किया गया है, जो (0.5f, 0.5f)
पर है. अगर आपने (0f, 0f)
पर ऑरिजिन तय किया है, तो ट्रांसफ़ॉर्मेशन, कॉम्पोज़ेबल के सबसे ऊपर बाएं कोने से शुरू होते हैं.
अगर rotationZ
ट्रांसफ़ॉर्मेशन की मदद से ऑरिजिन बदला जाता है, तो आपको दिखेगा कि आइटम, कॉम्पोज़ेबल के सबसे ऊपर बाईं ओर घूमता है:
Image( painter = painterResource(id = R.drawable.sunset), contentDescription = "Sunset", modifier = Modifier .graphicsLayer { this.transformOrigin = TransformOrigin(0f, 0f) this.rotationX = 90f this.rotationY = 275f this.rotationZ = 180f } )
क्लिप और आकार
आकार से उस आउटलाइन के बारे में पता चलता है जिस पर clip = true
होने पर कॉन्टेंट क्लिप होता है. इस उदाहरण में, हमने दो अलग-अलग क्लिप के लिए दो बॉक्स सेट किए हैं - एक में graphicsLayer
क्लिप वैरिएबल का इस्तेमाल किया गया है और दूसरे में, आसानी से इस्तेमाल किए जा सकने वाले रैपर Modifier.clip
का इस्तेमाल किया गया है.
Column(modifier = Modifier.padding(16.dp)) { Box( modifier = Modifier .size(200.dp) .graphicsLayer { clip = true shape = CircleShape } .background(Color(0xFFF06292)) ) { Text( "Hello Compose", style = TextStyle(color = Color.Black, fontSize = 46.sp), modifier = Modifier.align(Alignment.Center) ) } Box( modifier = Modifier .size(200.dp) .clip(CircleShape) .background(Color(0xFF4DB6AC)) ) }
पहले बॉक्स का कॉन्टेंट (जिसमें “Hello Compose” लिखा है) को सर्कल के आकार में काटा गया है:
इसके बाद, सबसे ऊपर मौजूद गुलाबी रंग के सर्कल पर translationY
लागू करने पर, आपको दिखेगा कि कॉम्पोज़ेबल के बाउंड अब भी पहले जैसे ही हैं. हालांकि, सर्कल सबसे नीचे मौजूद सर्कल के नीचे (और उसके बाउंड के बाहर) दिखता है.
कंपोज़ेबल को जिस हिस्से में बनाया गया है उस पर क्लिप बनाने के लिए, मॉडिफ़ायर चेन की शुरुआत में एक और Modifier.clip(RectangleShape)
जोड़ा जा सकता है. इसके बाद, कॉन्टेंट ओरिजनल बॉउंड में ही रहेगा.
Column(modifier = Modifier.padding(16.dp)) { Box( modifier = Modifier .clip(RectangleShape) .size(200.dp) .border(2.dp, Color.Black) .graphicsLayer { clip = true shape = CircleShape translationY = 50.dp.toPx() } .background(Color(0xFFF06292)) ) { Text( "Hello Compose", style = TextStyle(color = Color.Black, fontSize = 46.sp), modifier = Modifier.align(Alignment.Center) ) } Box( modifier = Modifier .size(200.dp) .clip(RoundedCornerShape(500.dp)) .background(Color(0xFF4DB6AC)) ) }
ऐल्फ़ा
Modifier.graphicsLayer
का इस्तेमाल, पूरी लेयर के लिए alpha
(अपारदर्शिता) सेट करने के लिए किया जा सकता है. 1.0f
पूरी तरह से अपारदर्शी है और 0.0f
नहीं दिखता.
Image( painter = painterResource(id = R.drawable.sunset), contentDescription = "clock", modifier = Modifier .graphicsLayer { this.alpha = 0.5f } )
कंपोज़िटिंग स्ट्रेटजी
ऐल्फ़ा और पारदर्शिता के साथ काम करना शायद किसी एक ऐल्फ़ा वैल्यू को बदलने जितना आसान
नहीं है. किसी अल्फा को बदलने के अलावा, graphicsLayer
पर CompositingStrategy
सेट करने का विकल्प भी होता है. CompositingStrategy
से यह तय होता है कि स्क्रीन पर पहले से मौजूद कॉन्टेंट के साथ, कॉम्पोज़ेबल का कॉन्टेंट कैसे कंपोज़ किया जाता है.
ये अलग-अलग रणनीतियां हैं:
अपने-आप चालू और बंद (डिफ़ॉल्ट)
कंपोज़िटिंग की रणनीति, बाकी graphicsLayer
पैरामीटर से तय होती है. अगर ऐल्फ़ा वैल्यू 1.0f से कम है या RenderEffect
सेट है, तो यह लेयर को ऑफ़स्क्रीन बफ़र में रेंडर करता है. जब भी अल्फा 1f से कम होता है, तो कॉन्टेंट को रेंडर करने के लिए, एक कॉम्पोज़िटिंग लेयर अपने-आप बन जाती है. इसके बाद, इस ऑफ़स्क्रीन बफ़र को उसी अल्फा के साथ डेस्टिनेशन पर ड्रॉ किया जाता है. RenderEffect
या ओवरस्क्रोल सेट करने पर, कॉन्टेंट हमेशा ऑफ़स्क्रीन बफ़र में रेंडर होता है. भले ही, CompositingStrategy
सेट किया गया हो या नहीं.
ऑफ़स्क्रीन
डेस्टिनेशन पर रेंडर करने से पहले, कॉम्पोज़ेबल के कॉन्टेंट को हमेशा ऑफ़स्क्रीन टेक्सचर या बिटमैप में रेस्टर किया जाता है. यह कॉन्टेंट को मास्क करने के लिए, BlendMode
ऑपरेशन लागू करने के लिए और ड्रॉइंग के निर्देशों के जटिल सेट को रेंडर करते समय परफ़ॉर्मेंस के लिए मददगार है.
BlendModes
के साथ CompositingStrategy.Offscreen
का इस्तेमाल करने का उदाहरण. नीचे दिए गए उदाहरण पर गौर करें. मान लें कि आपको BlendMode.Clear
का इस्तेमाल करने वाले ड्रॉ कमांड का इस्तेमाल करके, Image
कॉम्पोज़ेबल के कुछ हिस्सों को हटाना है. अगर compositingStrategy
को CompositingStrategy.Offscreen
पर सेट नहीं किया जाता है, तो BlendMode
अपने नीचे मौजूद सभी कॉन्टेंट के साथ इंटरैक्ट करता है.
Image( painter = painterResource(id = R.drawable.dog), contentDescription = "Dog", contentScale = ContentScale.Crop, modifier = Modifier .size(120.dp) .aspectRatio(1f) .background( Brush.linearGradient( listOf( Color(0xFFC5E1A5), Color(0xFF80DEEA) ) ) ) .padding(8.dp) .graphicsLayer { compositingStrategy = CompositingStrategy.Offscreen } .drawWithCache { val path = Path() path.addOval( Rect( topLeft = Offset.Zero, bottomRight = Offset(size.width, size.height) ) ) onDrawWithContent { clipPath(path) { // this draws the actual image - if you don't call drawContent, it wont // render anything this@onDrawWithContent.drawContent() } val dotSize = size.width / 8f // Clip a white border for the content drawCircle( Color.Black, radius = dotSize, center = Offset( x = size.width - dotSize, y = size.height - dotSize ), blendMode = BlendMode.Clear ) // draw the red circle indication drawCircle( Color(0xFFEF5350), radius = dotSize * 0.8f, center = Offset( x = size.width - dotSize, y = size.height - dotSize ) ) } } )
CompositingStrategy
को Offscreen
पर सेट करने से, निर्देशों को लागू करने के लिए ऑफ़स्क्रीन टेक्स्चर बनता है. इसमें BlendMode
को सिर्फ़ इस कॉम्पोज़ेबल के कॉन्टेंट पर लागू किया जाता है. इसके बाद, इसे स्क्रीन पर पहले से रेंडर किए गए कॉन्टेंट के ऊपर रेंडर किया जाता है. इससे पहले से रेंडर किए गए कॉन्टेंट पर कोई असर नहीं पड़ता.
अगर आपने CompositingStrategy.Offscreen
का इस्तेमाल नहीं किया है, तो BlendMode.Clear
का इस्तेमाल करने पर, डेस्टिनेशन के सभी पिक्सल मिट जाते हैं. भले ही, पहले से क्या सेट किया गया हो. इससे विंडो का रेंडरिंग बफ़र (काला) दिखता रहता है. अल्फा वाले कई BlendModes
, ऑफ़स्क्रीन बफ़र के बिना उम्मीद के मुताबिक काम नहीं करेंगे. लाल सर्कल इंडिकेटर के चारों ओर काली रिंग पर ध्यान दें:
इसे थोड़ा और समझने के लिए: अगर ऐप्लिकेशन में पारदर्शी विंडो का बैकग्राउंड है और आपने CompositingStrategy.Offscreen
का इस्तेमाल नहीं किया है, तो BlendMode
पूरे ऐप्लिकेशन के साथ इंटरैक्ट करेगा. यह ऐप्लिकेशन या वॉलपेपर दिखाने के लिए, सभी पिक्सल को हटा देगा, जैसा कि इस उदाहरण में दिखाया गया है:
ध्यान दें कि CompositingStrategy.Offscreen
का इस्तेमाल करने पर, ड्रॉइंग वाले हिस्से के साइज़ के हिसाब से एक ऑफ़स्क्रीन टेक्स्चर बनाया जाता है और उसे फिर से स्क्रीन पर रेंडर किया जाता है. इस रणनीति का इस्तेमाल करके किए जाने वाले ड्रॉइंग निर्देश, डिफ़ॉल्ट रूप से
इस इलाके में क्लिप किए जाएंगे. नीचे दिया गया कोड स्निपेट, ऑफ़स्क्रीन टेक्सचर का इस्तेमाल करते समय
अंतर दिखाता है:
@Composable fun CompositingStrategyExamples() { Column( modifier = Modifier .fillMaxSize() .wrapContentSize(Alignment.Center) ) { // Does not clip content even with a graphics layer usage here. By default, graphicsLayer // does not allocate + rasterize content into a separate layer but instead is used // for isolation. That is draw invalidations made outside of this graphicsLayer will not // re-record the drawing instructions in this composable as they have not changed Canvas( modifier = Modifier .graphicsLayer() .size(100.dp) // Note size of 100 dp here .border(2.dp, color = Color.Blue) ) { // ... and drawing a size of 200 dp here outside the bounds drawRect(color = Color.Magenta, size = Size(200.dp.toPx(), 200.dp.toPx())) } Spacer(modifier = Modifier.size(300.dp)) /* Clips content as alpha usage here creates an offscreen buffer to rasterize content into first then draws to the original destination */ Canvas( modifier = Modifier // force to an offscreen buffer .graphicsLayer(compositingStrategy = CompositingStrategy.Offscreen) .size(100.dp) // Note size of 100 dp here .border(2.dp, color = Color.Blue) ) { /* ... and drawing a size of 200 dp. However, because of the CompositingStrategy.Offscreen usage above, the content gets clipped */ drawRect(color = Color.Red, size = Size(200.dp.toPx(), 200.dp.toPx())) } } }
ModulateAlpha
कंपोज़िशन की इस रणनीति से, graphicsLayer
में रिकॉर्ड किए गए हर ड्रॉइंग निर्देश के लिए अल्फा को मॉड्यूलेट किया जाता है. यह 1.0f से कम अल्फा के लिए, ऑफ़स्क्रीन बफ़र तब तक नहीं बनाएगा, जब तक RenderEffect
सेट नहीं किया जाता. इससे अल्फा रेंडरिंग के लिए, यह ज़्यादा असरदार हो सकता है. हालांकि, ओवरलैप होने वाले कॉन्टेंट
के लिए, इससे अलग-अलग नतीजे मिल सकते हैं. जिन इस्तेमाल के उदाहरणों में पहले से पता होता है कि कॉन्टेंट ओवरलैप नहीं हो रहा है उनके लिए, यह CompositingStrategy.Auto
के मुकाबले बेहतर परफ़ॉर्म कर सकता है. इसके लिए, अल्फा वैल्यू 1 से कम होनी चाहिए.
कंपोज़ेबल के लिए अलग-अलग रणनीतियों का एक और उदाहरण नीचे दिया गया है - कंपोज़ेबल के अलग-अलग हिस्सों के लिए अलग-अलग अक्षर का इस्तेमाल करना और Modulate
की रणनीति लागू करना:
@Preview @Composable fun CompositingStrategy_ModulateAlpha() { Column( modifier = Modifier .fillMaxSize() .padding(32.dp) ) { // Base drawing, no alpha applied Canvas( modifier = Modifier.size(200.dp) ) { drawSquares() } Spacer(modifier = Modifier.size(36.dp)) // Alpha 0.5f applied to whole composable Canvas( modifier = Modifier .size(200.dp) .graphicsLayer { alpha = 0.5f } ) { drawSquares() } Spacer(modifier = Modifier.size(36.dp)) // 0.75f alpha applied to each draw call when using ModulateAlpha Canvas( modifier = Modifier .size(200.dp) .graphicsLayer { compositingStrategy = CompositingStrategy.ModulateAlpha alpha = 0.75f } ) { drawSquares() } } } private fun DrawScope.drawSquares() { val size = Size(100.dp.toPx(), 100.dp.toPx()) drawRect(color = Red, size = size) drawRect( color = Purple, size = size, topLeft = Offset(size.width / 4f, size.height / 4f) ) drawRect( color = Yellow, size = size, topLeft = Offset(size.width / 4f * 2f, size.height / 4f * 2f) ) } val Purple = Color(0xFF7E57C2) val Yellow = Color(0xFFFFCA28) val Red = Color(0xFFEF5350)
किसी कॉम्पोज़ेबल के कॉन्टेंट को बिटमैप में लिखना
आम तौर पर, किसी कॉम्पोज़ेबल से Bitmap
बनाने के लिए, यह तरीका अपनाया जाता है. अपने कॉम्पोज़ेबल के कॉन्टेंट को Bitmap
में कॉपी करने के लिए, rememberGraphicsLayer()
का इस्तेमाल करके GraphicsLayer
बनाएं.
drawWithContent()
और
graphicsLayer.record{}
का इस्तेमाल करके, ड्रॉइंग के निर्देशों को नई लेयर पर रीडायरेक्ट करें. इसके बाद, drawLayer
का इस्तेमाल करके, दिखने वाले कैनवस में लेयर बनाएं:
val coroutineScope = rememberCoroutineScope() val graphicsLayer = rememberGraphicsLayer() Box( modifier = Modifier .drawWithContent { // call record to capture the content in the graphics layer graphicsLayer.record { // draw the contents of the composable into the graphics layer this@drawWithContent.drawContent() } // draw the graphics layer on the visible canvas drawLayer(graphicsLayer) } .clickable { coroutineScope.launch { val bitmap = graphicsLayer.toImageBitmap() // do something with the newly acquired bitmap } } .background(Color.White) ) { Text("Hello Android", fontSize = 26.sp) }
बिट मैप को डिस्क पर सेव किया जा सकता है और शेयर किया जा सकता है. ज़्यादा जानकारी के लिए, पूरा उदाहरण स्निपेट देखें. डिस्क पर सेव करने की कोशिश करने से पहले, डिवाइस पर अनुमतियां देखना न भूलें.
कस्टम ड्रॉइंग मॉडिफ़ायर
अपना कस्टम मॉडिफ़ायर बनाने के लिए, DrawModifier
इंटरफ़ेस लागू करें. इससे आपको ContentDrawScope
का ऐक्सेस मिलता है, जो Modifier.drawWithContent()
का इस्तेमाल करने पर दिखने वाले ContentDrawScope
जैसा ही होता है. इसके बाद, कोड को बेहतर बनाने और आसान व्रपर उपलब्ध कराने के लिए, कस्टम ड्रॉइंग मॉडिफ़ायर में ड्रॉइंग के सामान्य ऑपरेशन निकाले जा सकते हैं. उदाहरण के लिए, Modifier.background()
एक आसान DrawModifier
है.
उदाहरण के लिए, अगर आपको ऐसा Modifier
बनाना है जो कॉन्टेंट को वर्टिकल तौर पर फ़्लिप करता हो, तो इसे इस तरह बनाया जा सकता है:
class FlippedModifier : DrawModifier { override fun ContentDrawScope.draw() { scale(1f, -1f) { this@draw.drawContent() } } } fun Modifier.flipped() = this.then(FlippedModifier())
इसके बाद, Text
पर लागू किए गए इस फ़्लिप् किए गए मॉडिफ़ायर का इस्तेमाल करें:
Text( "Hello Compose!", modifier = Modifier .flipped() )
अन्य संसाधन
graphicsLayer
और कस्टम ड्रॉइंग का इस्तेमाल करने के ज़्यादा उदाहरणों के लिए, ये संसाधन देखें:
आपके लिए सुझाव
- ध्यान दें: JavaScript बंद होने पर लिंक टेक्स्ट दिखता है
- कंपोज़ में ग्राफ़िक्स
- इमेज को पसंद के मुताबिक बनाना {:#customize-image}
- Jetpack Compose के लिए Kotlin