تُبرز الظلال واجهة المستخدم بشكل مرئي، وتوضّح للمستخدمين إمكانية التفاعل مع العناصر، وتقدّم ملاحظات فورية حول إجراءاتهم. توفّر أداة Compose عدة طرق لدمج الظلال في تطبيقك:
-
Modifier.shadow(): تنشئ هذه السمة ظلاً يستند إلى الارتفاع خلف دالة مركّبة يتوافق مع إرشادات "التصميم المتعدد الأبعاد". -
Modifier.dropShadow(): تنشئ هذه السمة ظلاً قابلاً للتخصيص يظهر خلف دالة مركّبة، ما يجعلها تبدو مرتفعة. Modifier.innerShadow(): تنشئ هذه السمة ظلاً داخل حدود دالة مركّبة، ما يجعلها تبدو مضغوطة على السطح خلفها.
يُعدّ Modifier.shadow() مناسبًا لإنشاء ظلال أساسية، بينما يوفّر المعدّلان dropShadow() وinnerShadow() تحكّمًا أكثر دقة في عرض الظلال.
توضّح هذه الصفحة كيفية تنفيذ كل من هذه المُعدِّلات، بما في ذلك كيفية
تحريك الظلال عند تفاعل المستخدم وكيفية ربط
المُعدِّلَين innerShadow() وdropShadow()
لإنشاء ظلال متدرّجة
وظلال نيومورفية والمزيد.
إنشاء ظلال أساسية
تنشئ السمة Modifier.shadow() ظلاً أساسيًا يتّبع إرشادات التصميم المتعدد الأبعاد يحاكي مصدر ضوء علوي. يعتمد عمق الظل على قيمة elevation، ويتم اقتصاص الظل المسقط على شكل الدالة المركّبة.
@Composable fun ElevationBasedShadow() { Box( modifier = Modifier.aspectRatio(1f).fillMaxSize(), contentAlignment = Alignment.Center ) { Box( Modifier .size(100.dp, 100.dp) .shadow(10.dp, RectangleShape) .background(Color.White) ) } }
Modifier.shadow().تنفيذ تظليل القطرات
استخدِم المُعدِّل dropShadow() لرسم ظل دقيق خلف
المحتوى، ما يجعل العنصر يبدو مرتفعًا.
يمكنك التحكّم في الجوانب الرئيسية التالية من خلال مَعلمة Shadow:
-
radius: تحدّد هذه السمة نعومة التمويه وانتشاره. -
color: تحدّد لون التظليل. -
offset: لتحديد موضع شكل الظل على طول المحورين "س" و"ص". -
spread: تتحكّم في توسيع أو تقليص هندسة الظل.
بالإضافة إلى ذلك، تحدّد المَعلمة shape الشكل العام للظل. ويمكنه استخدام أي شكل هندسي من حزمة androidx.compose.foundation.shape، بالإضافة إلى أشكال Material Expressive.
لتطبيق تظليل قطرات أساسي، أضِف المُعدِّل dropShadow() إلى سلسلة الدوال المركّبة، مع تحديد نصف القطر واللون والانتشار. يُرجى العِلم أنّ الخلفية purpleColor التي تظهر أعلى الظل يتم رسمها بعد المُعدِّل dropShadow():
@Composable fun SimpleDropShadowUsage() { Box(Modifier.fillMaxSize()) { Box( Modifier .width(300.dp) .height(300.dp) .dropShadow( shape = RoundedCornerShape(20.dp), shadow = Shadow( radius = 10.dp, spread = 6.dp, color = Color(0x40000000), offset = DpOffset(x = 4.dp, 4.dp) ) ) .align(Alignment.Center) .background( color = Color.White, shape = RoundedCornerShape(20.dp) ) ) { Text( "Drop Shadow", modifier = Modifier.align(Alignment.Center), fontSize = 32.sp ) } } }
نقاط رئيسية حول الرمز
- يتم تطبيق المُعدِّل
dropShadow()علىBoxالداخلي. يتميّز الظل بالخصائص التالية:- شكل مستطيل مستدير الزوايا (
RoundedCornerShape(20.dp)) - نصف قطر تمويه يبلغ
10.dp، ما يجعل الحواف ناعمة وموزّعة - انتشار بقيمة
6.dp، ما يؤدي إلى توسيع حجم الظل وجعله أكبر من المربّع الذي يلقيه - قيمة ألفا تبلغ
0.5f، ما يجعل الظل شبه شفاف
- شكل مستطيل مستدير الزوايا (
- بعد تحديد الظل،تم تطبيق معدِّل
background().- يتم ملء
Boxباللون الأبيض. - يتم اقتصاص الخلفية إلى شكل المستطيل نفسه ذي الزوايا الدائرية مثل الظل.
- يتم ملء
النتيجة
تنفيذ الظلال الداخلية
لإنشاء تأثير عكسي لـ dropShadow()، استخدِم
Modifier.innerShadow()، ما يخلق الوهم بأنّ العنصر
مجوّف أو مضغوط في السطح السفلي.
يكون الترتيب مهمًا عند إنشاء الظلال الداخلية. يتم رسم المُعدِّل innerShadow()
أعلى المحتوى. للتأكد من أن الظل مرئي، فإنك عادةً
ما تقوم بالخطوات التالية:
- ارسم محتوى الخلفية.
- طبِّق المُعدِّل
innerShadow()لإنشاء المظهر المقعّر.
إذا تم وضع innerShadow() قبل الخلفية، يتم رسم الخلفية
فوق الظل، ما يؤدي إلى إخفائه تمامًا.
يوضّح المثال التالي تطبيق innerShadow() على RoundedCornerShape:
@Composable fun SimpleInnerShadowUsage() { Box(Modifier.fillMaxSize()) { Box( Modifier .width(300.dp) .height(200.dp) .align(Alignment.Center) // note that the background needs to be defined before defining the inner shadow .background( color = Color.White, shape = RoundedCornerShape(20.dp) ) .innerShadow( shape = RoundedCornerShape(20.dp), shadow = Shadow( radius = 10.dp, spread = 2.dp, color = Color(0x40000000), offset = DpOffset(x = 6.dp, 7.dp) ) ) ) { Text( "Inner Shadow", modifier = Modifier.align(Alignment.Center), fontSize = 32.sp ) } } }
Modifier.innerShadow() على مستطيل بزوايا مستديرةتحريك الظلال عند تفاعل المستخدم
لجعل الظلال تستجيب لتفاعلات المستخدمين، يمكنك دمج خصائص الظل مع واجهات برمجة التطبيقات الخاصة بالحركة في Compose. عندما يضغط المستخدم على زر، على سبيل المثال، يمكن أن يتغير الظل لتقديم ملاحظات مرئية فورية.
ينشئ الرمز التالي تأثير "الضغط" مع ظل (وهم بأنّ السطح يتم دفعه لأسفل الشاشة):
@Composable fun AnimatedColoredShadows() { SnippetsTheme { Box(Modifier.fillMaxSize()) { val interactionSource = remember { MutableInteractionSource() } val isPressed by interactionSource.collectIsPressedAsState() // Create transition with pressed state val transition = updateTransition( targetState = isPressed, label = "button_press_transition" ) fun <T> buttonPressAnimation() = tween<T>( durationMillis = 400, easing = EaseInOut ) // Animate all properties using the transition val shadowAlpha by transition.animateFloat( label = "shadow_alpha", transitionSpec = { buttonPressAnimation() } ) { pressed -> if (pressed) 0f else 1f } // ... val blueDropShadow by transition.animateColor( label = "shadow_color", transitionSpec = { buttonPressAnimation() } ) { pressed -> if (pressed) Color.Transparent else blueDropShadowColor } // ... Box( Modifier .clickable( interactionSource, indication = null ) { // ** ...... **// } .width(300.dp) .height(200.dp) .align(Alignment.Center) .dropShadow( shape = RoundedCornerShape(70.dp), shadow = Shadow( radius = 10.dp, spread = 0.dp, color = blueDropShadow, offset = DpOffset(x = 0.dp, -(2).dp), alpha = shadowAlpha ) ) .dropShadow( shape = RoundedCornerShape(70.dp), shadow = Shadow( radius = 10.dp, spread = 0.dp, color = darkBlueDropShadow, offset = DpOffset(x = 2.dp, 6.dp), alpha = shadowAlpha ) ) // note that the background needs to be defined before defining the inner shadow .background( color = Color(0xFFFFFFFF), shape = RoundedCornerShape(70.dp) ) .innerShadow( shape = RoundedCornerShape(70.dp), shadow = Shadow( radius = 8.dp, spread = 4.dp, color = innerShadowColor2, offset = DpOffset(x = 4.dp, 0.dp) ) ) .innerShadow( shape = RoundedCornerShape(70.dp), shadow = Shadow( radius = 20.dp, spread = 4.dp, color = innerShadowColor1, offset = DpOffset(x = 4.dp, 0.dp), alpha = innerShadowAlpha ) ) ) { Text( "Animated Shadows", // ... ) } } } }
نقاط رئيسية حول الرمز
- تحدّد هذه السمة حالات البدء والانتهاء للمَعلمات التي سيتم تحريكها عند الضغط
باستخدام
transition.animateColorوtransition.animateFloat. - يستخدم
updateTransitionويوفّر لهtargetState (targetState = isPressed)المحدّد للتحقّق من مزامنة جميع الصور المتحركة. عندما يتغيّرisPressed، يدير عنصر الانتقال تلقائيًا الحركة لجميع الخصائص الفرعية من قيمها الحالية إلى القيم المستهدفة الجديدة. - تحدّد هذه السمة مواصفات
buttonPressAnimationالتي تتحكّم في توقيت الانتقال وتخفيفه. وتحدّد هذه السمةtween(اختصار لـ in-between) بمدة 400 ملي ثانية ومنحنىEaseInOut، ما يعني أنّ الحركة تبدأ ببطء، ثم تتسارع في المنتصف، ثم تتباطأ في النهاية. - تحدّد هذه السمة
Boxمع سلسلة من دوال التعديل التي تطبّق جميع الخصائص المتحركة لإنشاء العنصر المرئي، بما في ذلك ما يلي:- .
clickable(): عنصر تعديل يجعلBoxتفاعليًا. -
.dropShadow(): يتم تطبيق تأثيرَي تظليل قطرات خارجيَين أولاً. يتم ربط خصائص اللون والشفافية بالقيم المتحركة (blueDropShadow، وما إلى ذلك) وإنشاء المظهر المرتفع الأولي. -
.innerShadow(): يتم رسم ظلَّين داخليَّين فوق الخلفية. ترتبط خصائصها بالمجموعة الأخرى من القيم المتحركة (innerShadowColor1، وما إلى ذلك) وتنشئ المظهر المسنن.
- .
النتيجة
إنشاء ظلال متدرّجة
لا تقتصر الظلال على الألوان الثابتة. تقبل واجهة برمجة التطبيقات Shadow API مَعلمة Brush، ما يتيح لك إنشاء ظلال متدرّجة.
Box( modifier = Modifier .width(240.dp) .height(200.dp) .dropShadow( shape = RoundedCornerShape(70.dp), shadow = Shadow( radius = 10.dp, spread = animatedSpread.dp, brush = Brush.sweepGradient( colors ), offset = DpOffset(x = 0.dp, y = 0.dp), alpha = animatedAlpha ) ) .clip(RoundedCornerShape(70.dp)) .background(Color(0xEDFFFFFF)), contentAlignment = Alignment.Center ) { Text( text = breathingText, color = Color.Black, style = MaterialTheme.typography.bodyLarge ) }
نقاط رئيسية حول الرمز
- تضيف السمة
dropShadow()ظلاً خلف المربّع. - يُلوّن
brush = Brush.sweepGradient(colors)الظل بتدرج يدور من خلال قائمة منcolorsالمحددة مسبقًا، مما يخلق تأثيرًا يشبه قوس قزح.
النتيجة
يمكنك استخدام فرشاة كظل لإنشاء تدرج dropShadow() مع تأثير "تنفس":
دمج الظلال
يمكنك الجمع بين معدّلات dropShadow() وinnerShadow() ووضعها في طبقات
لإنشاء مجموعة متنوعة من التأثيرات. توضّح لك الأقسام التالية كيفية إنشاء ظلال ناعمة وواقعية وبسيطة باستخدام هذه التقنية.
إنشاء ظلال نيومورفيك
تتميز الظلال النيومورفية بمظهر ناعم يظهر بشكل طبيعي من الخلفية. لإنشاء ظلال نيومورفيك، اتّبِع الخطوات التالية:
- استخدِم عنصرًا يتشارك الألوان نفسها مع خلفيته.
- استخدِم تظليلَين خفيفَين متعاكسَين: ظل فاتح في إحدى الزوايا، وظل داكن في الزاوية المقابلة.
يضع المقتطف التالي طبقتَين من معدِّل dropShadow() لإنشاء تأثير النيومورفيزم:
@Composable fun NeumorphicRaisedButton( shape: RoundedCornerShape = RoundedCornerShape(30.dp) ) { val bgColor = Color(0xFFe0e0e0) val lightShadow = Color(0xFFFFFFFF) val darkShadow = Color(0xFFb1b1b1) val upperOffset = -10.dp val lowerOffset = 10.dp val radius = 15.dp val spread = 0.dp Box( modifier = Modifier .fillMaxSize() .background(bgColor) .wrapContentSize(Alignment.Center) .size(240.dp) .dropShadow( shape, shadow = Shadow( radius = radius, color = lightShadow, spread = spread, offset = DpOffset(upperOffset, upperOffset) ), ) .dropShadow( shape, shadow = Shadow( radius = radius, color = darkShadow, spread = spread, offset = DpOffset(lowerOffset, lowerOffset) ), ) .background(bgColor, shape) ) }
إنشاء ظلال نيوبروتالية
يعرض أسلوب neobrutalist تخطيطات عالية التباين وممتلئة وألوانًا زاهية
وحدودًا سميكة. لإنشاء هذا التأثير، استخدِم dropShadow() بدون تمويه
وإزاحة مميّزة، كما هو موضّح في المقتطف التالي:
@Composable fun NeoBrutalShadows() { SnippetsTheme { val dropShadowColor = Color(0xFF007AFF) val borderColor = Color(0xFFFF2D55) Box(Modifier.fillMaxSize()) { Box( Modifier .width(300.dp) .height(200.dp) .align(Alignment.Center) .dropShadow( shape = RoundedCornerShape(0.dp), shadow = Shadow( radius = 0.dp, spread = 0.dp, color = dropShadowColor, offset = DpOffset(x = 8.dp, 8.dp) ) ) .border( 8.dp, borderColor ) .background( color = Color.White, shape = RoundedCornerShape(0.dp) ) ) { Text( "Neobrutal Shadows", modifier = Modifier.align(Alignment.Center), style = MaterialTheme.typography.bodyMedium ) } } } }
إنشاء ظلال واقعية
تحاكي الظلال الواقعية الظلال في العالم المادي - تظهر مضاءة بمصدر ضوء أساسي، مما ينتج عنه ظل مباشر وظل أكثر انتشارًا. يمكنك تجميع عدة نُسخ من dropShadow() وinnerShadow() بسمات مختلفة لإعادة إنشاء تأثيرات ظل واقعية، كما هو موضّح في المقتطف التالي:
@Composable fun RealisticShadows() { Box(Modifier.fillMaxSize()) { val dropShadowColor1 = Color(0xB3000000) val dropShadowColor2 = Color(0x66000000) val innerShadowColor1 = Color(0xCC000000) val innerShadowColor2 = Color(0xFF050505) val innerShadowColor3 = Color(0x40FFFFFF) val innerShadowColor4 = Color(0x1A050505) Box( Modifier .width(300.dp) .height(200.dp) .align(Alignment.Center) .dropShadow( shape = RoundedCornerShape(100.dp), shadow = Shadow( radius = 40.dp, spread = 0.dp, color = dropShadowColor1, offset = DpOffset(x = 2.dp, 8.dp) ) ) .dropShadow( shape = RoundedCornerShape(100.dp), shadow = Shadow( radius = 4.dp, spread = 0.dp, color = dropShadowColor2, offset = DpOffset(x = 0.dp, 4.dp) ) ) // note that the background needs to be defined before defining the inner shadow .background( color = Color.Black, shape = RoundedCornerShape(100.dp) ) // // .innerShadow( shape = RoundedCornerShape(100.dp), shadow = Shadow( radius = 12.dp, spread = 3.dp, color = innerShadowColor1, offset = DpOffset(x = 6.dp, 6.dp) ) ) .innerShadow( shape = RoundedCornerShape(100.dp), shadow = Shadow( radius = 4.dp, spread = 1.dp, color = Color.White, offset = DpOffset(x = 5.dp, 5.dp) ) ) .innerShadow( shape = RoundedCornerShape(100.dp), shadow = Shadow( radius = 12.dp, spread = 5.dp, color = innerShadowColor2, offset = DpOffset(x = (-3).dp, (-12).dp) ) ) .innerShadow( shape = RoundedCornerShape(100.dp), shadow = Shadow( radius = 3.dp, spread = 10.dp, color = innerShadowColor3, offset = DpOffset(x = 0.dp, 0.dp) ) ) .innerShadow( shape = RoundedCornerShape(100.dp), shadow = Shadow( radius = 3.dp, spread = 9.dp, color = innerShadowColor4, offset = DpOffset(x = 1.dp, 1.dp) ) ) ) { Text( "Realistic Shadows", modifier = Modifier.align(Alignment.Center), fontSize = 24.sp, color = Color.White ) } } }
نقاط رئيسية حول الرمز
- يتم تطبيق مفتاحَي تعديل
dropShadow()متسلسلَين بسمات مختلفة، يليهما مفتاح تعديلbackground(). - يتم تطبيق معدِّلات
innerShadow()المتسلسلة لإنشاء تأثير الحافة المعدنية حول حافة المكوّن.
النتيجة
ينتج عن مقتطف الرمز السابق ما يلي: