Compose में Canvas कंपोज़ेबल के अलावा, काम के कई ग्राफ़िक Modifiers होते हैं. इनकी मदद से, कस्टम कॉन्टेंट बनाया जा सकता है. ये मॉडिफ़ायर इसलिए काम के हैं, क्योंकि इन्हें किसी भी कंपोज़ेबल पर लागू किया जा सकता है.
ड्रॉइंग मॉडिफ़ायर
Compose में, ड्राइंग से जुड़े सभी निर्देश, ड्राइंग मॉडिफ़ायर की मदद से दिए जाते हैं. Compose में ड्राइंग के तीन मुख्य मॉडिफ़ायर होते हैं:
ड्रॉइंग के लिए बेस मॉडिफ़ायर drawWithContent है. इसमें यह तय किया जा सकता है कि आपके कंपोज़ेबल को किस क्रम में ड्रॉ किया जाए. साथ ही, मॉडिफ़ायर के अंदर ड्रॉइंग के लिए कौनसे निर्देश दिए जाएं. 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 ऑपरेशन लागू करने में मददगार होता है. साथ ही, यह ड्राइंग के निर्देशों के जटिल सेट को रेंडर करने के दौरान परफ़ॉर्मेंस को बेहतर बनाने में भी मदद करता है.
CompositingStrategy.Offscreen का इस्तेमाल करने का एक उदाहरण BlendModes के साथ दिया गया है. यहां दिए गए उदाहरण में, मान लें कि आपको Image की मदद से बनाए गए किसी कॉम्पोज़ेबल के कुछ हिस्सों को हटाना है. इसके लिए, आपको एक ड्रॉ कमांड जारी करनी होगी, जो BlendMode.Clear का इस्तेमाल करती है. अगर आपने 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 का इस्तेमाल नहीं किया है, तो 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