Canvas कंपोज़ेबल के अलावा, Compose में कई काम के ग्राफ़िक्स
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 } )
अनुवाद
graphicsLayer का इस्तेमाल करके, translationX और translationY में बदलाव किया जा सकता है.
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 } )
ऑरिजिन
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 के साथ है.
नीचे दिए गए उदाहरण में, मान लें कि आपको 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 सेट किया गया है, तो यह ऐल्फ़ा रेंडरिंग के लिए ज़्यादा असरदार हो सकता है. हालांकि, ओवरलैप होने वाले कॉन्टेंट के लिए, यह अलग-अलग नतीजे दिखा सकता है. ऐसे मामलों में जहां पहले से पता हो कि कॉन्टेंट ओवरलैप नहीं हो रहा है, वहां 1 से कम ऐल्फ़ा वैल्यू वाले CompositingStrategy.Auto की तुलना में, यह बेहतर परफ़ॉर्मेंस दे सकता है.
यहां दिए गए उदाहरण में, कंपोज़िशन की अलग-अलग रणनीतियां दिखाई गई हैं. इनमें कंपोज़ेबल के अलग-अलग हिस्सों पर अलग-अलग ऐल्फ़ा लागू करना और 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) ) } @Preview @Composable fun ModifierGraphicsLayerOutsets() { Column( modifier = Modifier .background(Color(0xFF0A0E14)) .padding(48.dp), verticalArrangement = Arrangement.spacedBy(48.dp), horizontalAlignment = Alignment.CenterHorizontally ) { val pillShape = RoundedCornerShape(percent = 50) // Without outsets: alpha = 0.5f creates an offscreen buffer that clips the 24.dp glow Box( modifier = Modifier .size( width = 140.dp, height = 56.dp ) .graphicsLayer( alpha = 0.5f, clip = false ) .dropShadow( shape = pillShape, shadow = Shadow(radius = 24.dp, color = Color.Cyan) ) .background(color = Color(0xFF141824), shape = pillShape), contentAlignment = Alignment.Center ) { Text( text = "✨ Glow", color = Color.White, fontSize = 18.sp ) } Box( modifier = Modifier .size( width = 140.dp, height = 56.dp ) .graphicsLayer( alpha = 0.5f, outsets = LayerOutsets(24.dp), clip = false ) .dropShadow( shape = pillShape, shadow = Shadow(radius = 24.dp, color = Color.Cyan) ) .background(color = Color(0xFF141824), shape = pillShape), contentAlignment = Alignment.Center ) { Text( text = "✨ Glow", color = Color.White, fontSize = 18.sp ) } } } val Purple = Color(0xFF7E57C2) val Yellow = Color(0xFFFFCA28) val Red = Color(0xFFEF5350)
लेयर आउटसेट
जब किसी कंपोज़ेबल को ऑफ़स्क्रीन बफ़र में ड्रॉ किया जाता है (उदाहरण के लिए, जब alpha, 1.0f से कम हो, RenderEffect सेट हो या CompositingStrategy.Offscreen का इस्तेमाल किया गया हो), तो ऑफ़स्क्रीन टेक्स्चर, डिफ़ॉल्ट रूप से कंपोज़ेबल के मेज़र किए गए लेआउट की सीमाओं पर सेट हो जाता है. लेयर के अंदर मौजूद कोई भी ड्रॉइंग या शैडो, जो कंपोज़ेबल के लेआउट की सीमाओं से बाहर तक फैली हो (जैसे कि Modifier.dropShadow से बनाई गई बाहरी चमक), उसे ऑफ़स्क्रीन बफ़र की सीमा पर अपने-आप क्लिप कर दिया जाता है.
Modifier.padding() जोड़ने के बजाय (जो आस-पास के यूज़र इंटरफ़ेस (यूआई) एलिमेंट के लेआउट मेज़रमेंट को शिफ़्ट करता है), LayerOutsets को Modifier.graphicsLayer के outsets पैरामीटर में पास करें. इससे, कंपोज़ेबल के मेज़र किए गए लेआउट साइज़ में बदलाव किए बिना, हर तरफ़ ऑफ़स्क्रीन बफ़र के विज़ुअल बाउंड को बड़ा किया जाता है. ध्यान दें कि
LayerOutsets, लेयर के क्लिप शेप या शैडो बाउंड को बड़ा नहीं करता है. ये
कंपोज़ेबल के ओरिजनल साइज़ के हिसाब से ही रहते हैं. इसलिए, ओरिजनल बाउंड पर क्लिपिंग से बचने के लिए, graphicsLayer पर clip = false
रखें:
Box( modifier = Modifier .size( width = 140.dp, height = 56.dp ) .graphicsLayer( alpha = 0.5f, outsets = LayerOutsets(24.dp), clip = false ) .dropShadow( shape = pillShape, shadow = Shadow(radius = 24.dp, color = Color.Cyan) ) .background(color = Color(0xFF141824), shape = pillShape), contentAlignment = Alignment.Center ) { Text( text = "✨ Glow", color = Color.White, fontSize = 18.sp ) }
LayerOutsets के बिना ऑफ़स्क्रीन लेयर की बाहरी चमक (सबसे ऊपर, कंपोज़ेबल के रेक्टैंगल लेआउट की सीमाओं पर क्लिप की गई) और LayerOutsets(24.dp) के साथ (सबसे नीचे, क्लिप नहीं की गई)किसी कंपोज़ेबल के कॉन्टेंट को बिट मैप में लिखना
इसका एक सामान्य उदाहरण, कंपोज़ेबल से 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() का इस्तेमाल करने पर मिलता है. इसके बाद, कोड को साफ़ करने और सुविधाजनक रैपर उपलब्ध कराने के लिए, ड्रॉइंग के सामान्य ऑपरेशन को कस्टम ड्रॉइंग मॉडिफ़ायर में बदला जा सकता है. उदाहरण के लिए, 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 बंद होने पर, लिंक टेक्स्ट दिखता है
- Compose में ग्राफ़िक
- किसी इमेज को पसंद के मुताबिक बनाना {:#customize-image}
- Jetpack Compose के लिए Kotlin