بالإضافة إلى الدالة المركّبة 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 إذا كانت
العناصر تُستخدم للرسم فقط.
على سبيل المثال، إذا أنشأت Brush لرسم تدرج خلف Text، يؤدي استخدام 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). يمكن إعادة إصدار تعليمات الرسم التي تم التقاطها كجزء من طبقة بكفاءة من خلال مسار العرض بدون إعادة تنفيذ الرمز البرمجي للتطبيق. - التحويلات التي تنطبق على جميع تعليمات الرسم الواردة في طبقة.
- تنقيط إمكانيات التركيب عند تحويل طبقة إلى صورة نقطية، يتم تنفيذ تعليمات الرسم الخاصة بها ويتم تسجيل الناتج في مخزن مؤقت خارج الشاشة. Compositing such a buffer for subsequent frames is faster than executing the individual instructions, but it will behave as a bitmap when transforms like scaling or rotation are applied.
عمليات التحويل
توفّر Modifier.graphicsLayer عزلًا لتعليمات الرسم،
على سبيل المثال، يمكن تطبيق عمليات تحويل مختلفة باستخدام
Modifier.graphicsLayer. ويمكن تحريك هذه العناصر أو تعديلها بدون الحاجة إلى إعادة تنفيذ دالة lambda الخاصة بالرسم.
لا يغيّر 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 للتدوير عموديًا، وrotationZ للتدوير على المحور Z (التدوير العادي). يتم تحديد هذه القيمة بالدرجات (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)) ) }
يتم قص محتوى المربّع الأول (النص الذي يقول "مرحبًا يا 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 } )
استراتيجية التركيب
إنّ العمل مع قناة ألفا والشفافية ليس دائمًا بسيطًا مثل تغيير قيمة ألفا واحدة. بالإضافة إلى تغيير قيمة ألفا، يمكنك أيضًا ضبط CompositingStrategy على graphicsLayer. يحدّد 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، سيتفاعل BlendMode مع التطبيق بأكمله. سيؤدي ذلك إلى محو جميع وحدات البكسل لإظهار التطبيق أو الخلفية تحته، كما في هذا المثال:
تجدر الإشارة إلى أنّه عند استخدام CompositingStrategy.Offscreen، يتم إنشاء مادة خارج الشاشة بحجم مساحة الرسم وعرضها مرة أخرى على الشاشة. Any drawing commands that are done with this strategy, are by default be
clipped to this region. يوضّح مقتطف الرمز التالي الاختلافات
عند التبديل إلى استخدام زخارف خارج الشاشة:
@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) ) } @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 إلى المَعلمة outsets في Modifier.graphicsLayer. يؤدي ذلك إلى توسيع الحدود المرئية للمخزن المؤقت خارج الشاشة على كل جانب بدون تغيير حجم التنسيق الذي تم قياسه للعنصر القابل للإنشاء. يُرجى العِلم أنّ
LayerOutsets لا يوسّع شكل الاقتصاص أو حدود الظل للطبقة، والتي
تظل مستندة إلى الحجم الأصلي الذي تم قياسه للدالة المركّبة (لذا يجب إبقاء clip = false
على graphicsLayer لتجنُّب الاقتصاص إلى الحدود الأصلية):
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، أنشِئ GraphicsLayer باستخدام rememberGraphicsLayer().
يمكنك إعادة توجيه أوامر الرسم إلى الطبقة الجديدة باستخدام 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 والرسم المخصّص، يمكنك مراجعة
المراجع التالية:
- تحريك قنديل البحر في Compose
- التخطيطات والرسومات في Compose في مؤتمر Android Developer Summit لعام 2022
التطبيقات المقترَحة لك
- ملاحظة: يتم عرض نص الرابط عندما يكون JavaScript غير مفعَّل
- الرسومات في Compose
- تخصيص صورة {:#customize-image}
- Kotlin لـ Jetpack Compose