Jetpack Compose 阴影效果

7 阅读12分钟

Jetpack Compose 阴影效果

目录

  1. 阴影基础概念
  2. 阴影使用指南
  3. 阴影设置选项详解
  4. 自定义阴影实现
  5. 最佳实践与性能优化

一、阴影基础概念

1.1 阴影在Compose中的工作原理

Jetpack Compose中的阴影是通过** elevation(海拔/高度)**系统实现的。阴影效果模拟了光线照射到 elevated 组件时产生的投影,是Material Design设计语言的核心视觉元素之一。

光线方向(默认左上)
       ↘
    ╔═══════╗
    ║       ║  ← elevated 组件
    ╚═══════╝
       ↘
        ╲ 阴影区域

1.2 阴影渲染层级

Compose阴影渲染流程:
┌─────────────────────────────────────┐
│  1. 确定组件形状 (Shape)             │
├─────────────────────────────────────┤
│  2. 计算阴影路径 (Shadow Path)       │
├─────────────────────────────────────┤
│  3. 应用模糊效果 (Blur)              │
├─────────────────────────────────────┤
│  4. 偏移阴影位置 (Offset)            │
├─────────────────────────────────────┤
│  5. 合成到画布 (Canvas Rendering)    │
└─────────────────────────────────────┘

1.3 关键属性概览

属性说明默认值适用API
elevation海拔高度,决定阴影大小0.dp全版本
shadowElevation阴影海拔(明确指定阴影)0.dpCompose 1.3+
ambientColor环境光阴影颜色Color.BlackCompose 1.2+
spotColor点光源阴影颜色Color.BlackCompose 1.2+
shape阴影形状RectangleShape全版本
clip是否裁剪阴影false全版本

1.4 系统默认行为

// 默认阴影行为
Box(
    modifier = Modifier
        .size(100.dp)
        .shadow(elevation = 8.dp)  // 默认黑色阴影,自动偏移
)

默认行为特点:

  • 阴影颜色:纯黑色 (Color.Black)
  • 阴影偏移:根据 elevation 自动计算(右下方向)
  • 模糊半径:与 elevation 成正比
  • 形状:使用组件的 shape 或默认矩形

二、阴影使用指南

2.1 基础阴影添加

2.1.1 使用 shadow Modifier
@Composable
fun BasicShadowExample() {
    Column(
        modifier = Modifier.padding(16.dp),
        verticalArrangement = Arrangement.spacedBy(16.dp)
    ) {
        // 基础阴影
        Box(
            modifier = Modifier
                .size(120.dp)
                .shadow(elevation = 4.dp)
                .background(Color.White)
        )
        
        // 中等阴影
        Box(
            modifier = Modifier
                .size(120.dp)
                .shadow(elevation = 8.dp)
                .background(Color.White)
        )
        
        // 强阴影
        Box(
            modifier = Modifier
                .size(120.dp)
                .shadow(elevation = 16.dp)
                .background(Color.White)
        )
    }
}
2.1.2 带形状的阴影
@Composable
fun ShapedShadowExample() {
    Row(
        modifier = Modifier.padding(16.dp),
        horizontalArrangement = Arrangement.spacedBy(16.dp)
    ) {
        // 圆形阴影
        Box(
            modifier = Modifier
                .size(100.dp)
                .shadow(
                    elevation = 8.dp,
                    shape = CircleShape
                )
                .background(Color.Blue, CircleShape)
        )
        
        // 圆角矩形阴影
        Box(
            modifier = Modifier
                .size(100.dp)
                .shadow(
                    elevation = 8.dp,
                    shape = RoundedCornerShape(16.dp)
                )
                .background(Color.Green, RoundedCornerShape(16.dp))
        )
        
        // 自定义形状阴影
        val triangleShape = GenericShape { size, _ ->
            moveTo(size.width / 2f, 0f)
            lineTo(size.width, size.height)
            lineTo(0f, size.height)
            close()
        }
        
        Box(
            modifier = Modifier
                .size(100.dp)
                .shadow(
                    elevation = 8.dp,
                    shape = triangleShape
                )
                .background(Color.Red, triangleShape)
        )
    }
}

2.2 调整阴影效果

2.2.1 阴影颜色定制(Compose 1.2+)
@Composable
fun ColoredShadowExample() {
    Column(
        modifier = Modifier.padding(16.dp),
        verticalArrangement = Arrangement.spacedBy(16.dp)
    ) {
        // 彩色阴影 - Material 3风格
        Box(
            modifier = Modifier
                .size(120.dp)
                .shadow(
                    elevation = 12.dp,
                    spotColor = Color(0xFF6200EE).copy(alpha = 0.5f),
                    ambientColor = Color(0xFF6200EE).copy(alpha = 0.3f),
                    shape = RoundedCornerShape(16.dp)
                )
                .background(Color.White, RoundedCornerShape(16.dp))
        )
        
        // 暖色调阴影
        Box(
            modifier = Modifier
                .size(120.dp)
                .shadow(
                    elevation = 12.dp,
                    spotColor = Color(0xFFFF6B35).copy(alpha = 0.4f),
                    ambientColor = Color(0xFFFF6B35).copy(alpha = 0.2f),
                    shape = RoundedCornerShape(16.dp)
                )
                .background(Color(0xFFFFF5F0), RoundedCornerShape(16.dp))
        )
        
        // 冷色调阴影
        Box(
            modifier = Modifier
                .size(120.dp)
                .shadow(
                    elevation = 12.dp,
                    spotColor = Color(0xFF00BCD4).copy(alpha = 0.4f),
                    ambientColor = Color(0xFF00BCD4).copy(alpha = 0.2f),
                    shape = RoundedCornerShape(16.dp)
                )
                .background(Color(0xFFE0F7FA), RoundedCornerShape(16.dp))
        )
    }
}
2.2.2 裁剪与非裁剪阴影
@Composable
fun ClipShadowExample() {
    Row(
        modifier = Modifier.padding(16.dp),
        horizontalArrangement = Arrangement.spacedBy(24.dp)
    ) {
        // 裁剪阴影(默认)- 阴影被形状边界裁剪
        Box(
            modifier = Modifier
                .size(100.dp)
                .shadow(
                    elevation = 12.dp,
                    shape = CircleShape,
                    clip = true  // 默认值
                )
                .background(Color.Blue, CircleShape)
        ) {
            Text(
                text = "Clip",
                color = Color.White,
                modifier = Modifier.align(Alignment.Center)
            )
        }
        
        // 非裁剪阴影 - 阴影超出形状边界
        Box(
            modifier = Modifier
                .size(100.dp)
                .shadow(
                    elevation = 12.dp,
                    shape = CircleShape,
                    clip = false  // 不裁剪
                )
                .background(Color.Red, CircleShape)
        ) {
            Text(
                text = "No Clip",
                color = Color.White,
                modifier = Modifier.align(Alignment.Center)
            )
        }
    }
}

2.3 移除阴影效果

@Composable
fun RemoveShadowExample() {
    Column(
        modifier = Modifier.padding(16.dp),
        verticalArrangement = Arrangement.spacedBy(16.dp)
    ) {
        // 方式1:设置elevation为0
        Box(
            modifier = Modifier
                .size(100.dp)
                .shadow(elevation = 0.dp)
                .background(Color.White)
        )
        
        // 方式2:使用graphicsLayer移除阴影
        Box(
            modifier = Modifier
                .size(100.dp)
                .graphicsLayer {
                    shadowElevation = 0f
                }
                .background(Color.White)
        )
        
        // 方式3:直接不使用shadow modifier
        Box(
            modifier = Modifier
                .size(100.dp)
                .background(Color.White)
        )
    }
}

三、阴影设置选项详解

3.1 shadow Modifier完整参数

fun Modifier.shadow(
    elevation: Dp,
    shape: Shape = RectangleShape,
    clip: Boolean = elevation > 0.dp,
    ambientColor: Color = DefaultShadowColor,  // Compose 1.2+
    spotColor: Color = DefaultShadowColor       // Compose 1.2+
): Modifier

3.2 参数详细说明

3.2.1 elevation(海拔高度)
@Composable
fun ElevationShowcase() {
    val elevations = listOf(0.dp, 2.dp, 4.dp, 8.dp, 16.dp, 32.dp)
    
    Column(
        modifier = Modifier.padding(16.dp),
        verticalArrangement = Arrangement.spacedBy(16.dp)
    ) {
        elevations.forEach { elevation ->
            Row(
                verticalAlignment = Alignment.CenterVertically,
                horizontalArrangement = Arrangement.spacedBy(16.dp)
            ) {
                Box(
                    modifier = Modifier
                        .size(80.dp)
                        .shadow(
                            elevation = elevation,
                            shape = RoundedCornerShape(8.dp)
                        )
                        .background(Color.White, RoundedCornerShape(8.dp))
                )
                
                Text(
                    text = "${elevation.value.toInt()}dp",
                    fontSize = 16.sp,
                    fontWeight = FontWeight.Medium
                )
            }
        }
    }
}

elevation与视觉效果对应关系:

ElevationMaterial Design层级适用场景
0.dp基础层背景、静态内容
1-2.dp卡片悬停轻微凸起
4-6.dp卡片、按钮可交互元素
8-12.dp应用栏、浮动按钮重要操作元素
16-24.dp对话框、抽屉模态内容
3.2.2 shape(形状)
@Composable
fun ShapeShadowShowcase() {
    Column(
        modifier = Modifier.padding(16.dp),
        verticalArrangement = Arrangement.spacedBy(16.dp)
    ) {
        // 矩形
        ShadowShapeBox(
            shape = RectangleShape,
            name = "RectangleShape"
        )
        
        // 圆角矩形
        ShadowShapeBox(
            shape = RoundedCornerShape(16.dp),
            name = "RoundedCornerShape(16.dp)"
        )
        
        // 圆形
        ShadowShapeBox(
            shape = CircleShape,
            name = "CircleShape"
        )
        
        // 胶囊形
        ShadowShapeBox(
            shape = RoundedCornerShape(50.dp),
            name = "Capsule Shape"
        )
        
        // 切角形状
        val cutCornerShape = CutCornerShape(
            topStart = 16.dp,
            topEnd = 0.dp,
            bottomEnd = 16.dp,
            bottomStart = 0.dp
        )
        ShadowShapeBox(
            shape = cutCornerShape,
            name = "CutCornerShape"
        )
        
        // 自定义形状
        val customShape = GenericShape { size, _ ->
            val path = Path().apply {
                moveTo(0f, 0f)
                lineTo(size.width * 0.8f, 0f)
                lineTo(size.width, size.height * 0.5f)
                lineTo(size.width * 0.8f, size.height)
                lineTo(0f, size.height)
                close()
            }
            addPath(path)
        }
        ShadowShapeBox(
            shape = customShape,
            name = "Custom Shape"
        )
    }
}

@Composable
private fun ShadowShapeBox(shape: Shape, name: String) {
    Row(
        verticalAlignment = Alignment.CenterVertically,
        horizontalArrangement = Arrangement.spacedBy(16.dp)
    ) {
        Box(
            modifier = Modifier
                .size(80.dp)
                .shadow(
                    elevation = 8.dp,
                    shape = shape
                )
                .background(Color(0xFF6200EE), shape)
        )
        
        Text(
            text = name,
            fontSize = 14.sp,
            color = Color.Gray
        )
    }
}
3.2.3 阴影颜色(ambientColor & spotColor)
@Composable
fun ShadowColorDeepDive() {
    Column(
        modifier = Modifier.padding(16.dp),
        verticalArrangement = Arrangement.spacedBy(24.dp)
    ) {
        // 环境光 vs 点光源阴影对比
        Text(
            text = "Ambient vs Spot Shadow",
            fontSize = 18.sp,
            fontWeight = FontWeight.Bold
        )
        
        Row(
            horizontalArrangement = Arrangement.spacedBy(16.dp)
        ) {
            // 仅环境光阴影
            ShadowColorBox(
                ambientColor = Color.Red.copy(alpha = 0.5f),
                spotColor = Color.Transparent,
                label = "Ambient Only"
            )
            
            // 仅点光源阴影
            ShadowColorBox(
                ambientColor = Color.Transparent,
                spotColor = Color.Red.copy(alpha = 0.5f),
                label = "Spot Only"
            )
            
            // 组合阴影
            ShadowColorBox(
                ambientColor = Color.Red.copy(alpha = 0.3f),
                spotColor = Color.Red.copy(alpha = 0.5f),
                label = "Combined"
            )
        }
        
        // 透明度影响
        Text(
            text = "Alpha Channel Impact",
            fontSize = 18.sp,
            fontWeight = FontWeight.Bold
        )
        
        Row(
            horizontalArrangement = Arrangement.spacedBy(16.dp)
        ) {
            ShadowColorBox(
                ambientColor = Color.Blue.copy(alpha = 0.1f),
                spotColor = Color.Blue.copy(alpha = 0.2f),
                label = "Alpha 0.1-0.2"
            )
            
            ShadowColorBox(
                ambientColor = Color.Blue.copy(alpha = 0.3f),
                spotColor = Color.Blue.copy(alpha = 0.5f),
                label = "Alpha 0.3-0.5"
            )
            
            ShadowColorBox(
                ambientColor = Color.Blue.copy(alpha = 0.6f),
                spotColor = Color.Blue.copy(alpha = 0.8f),
                label = "Alpha 0.6-0.8"
            )
        }
    }
}

@Composable
private fun ShadowColorBox(
    ambientColor: Color,
    spotColor: Color,
    label: String
) {
    Column(
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.spacedBy(8.dp)
    ) {
        Box(
            modifier = Modifier
                .size(80.dp)
                .shadow(
                    elevation = 12.dp,
                    shape = RoundedCornerShape(8.dp),
                    ambientColor = ambientColor,
                    spotColor = spotColor
                )
                .background(Color.White, RoundedCornerShape(8.dp))
        )
        
        Text(
            text = label,
            fontSize = 12.sp,
            color = Color.Gray
        )
    }
}

3.3 使用 graphicsLayer 实现高级阴影

@Composable
fun GraphicsLayerShadowExample() {
    Column(
        modifier = Modifier.padding(16.dp),
        verticalArrangement = Arrangement.spacedBy(16.dp)
    ) {
        // 基础 graphicsLayer 阴影
        Box(
            modifier = Modifier
                .size(120.dp)
                .graphicsLayer {
                    shadowElevation = 20.dp.toPx()
                    shape = RoundedCornerShape(16.dp)
                    clip = true
                }
                .background(Color.White)
        )
        
        // 带透明度的阴影
        Box(
            modifier = Modifier
                .size(120.dp)
                .graphicsLayer {
                    shadowElevation = 20.dp.toPx()
                    alpha = 0.9f
                    shape = CircleShape
                    clip = true
                }
                .background(Color(0xFF6200EE))
        )
        
        // 组合变换与阴影
        Box(
            modifier = Modifier
                .size(120.dp)
                .graphicsLayer {
                    shadowElevation = 30.dp.toPx()
                    rotationZ = 15f
                    scaleX = 1.1f
                    scaleY = 1.1f
                    shape = RoundedCornerShape(20.dp)
                    clip = true
                }
                .background(
                    brush = Brush.linearGradient(
                        colors = listOf(Color(0xFF667eea), Color(0xFF764ba2))
                    ),
                    shape = RoundedCornerShape(20.dp)
                )
        )
    }
}

四、自定义阴影实现

4.1 自定义阴影绘制逻辑

@Composable
fun CustomShadowDrawing() {
    Column(
        modifier = Modifier.padding(16.dp),
        verticalArrangement = Arrangement.spacedBy(24.dp)
    ) {
        // 自定义模糊阴影
        CustomBlurShadow()
        
        // 内阴影效果
        InnerShadow()
        
        // 多层阴影
        MultiLayerShadow()
        
        // 渐变阴影
        GradientShadow()
    }
}

/**
 * 自定义模糊阴影 - 使用BlurMaskFilter
 */
@Composable
private fun CustomBlurShadow() {
    Box(
        modifier = Modifier
            .size(150.dp)
            .drawBehind {
                val paint = Paint().apply {
                    color = Color(0xFF6200EE).copy(alpha = 0.3f)
                    asFrameworkPaint().apply {
                        maskFilter = BlurMaskFilter(
                            30f,
                            BlurMaskFilter.Blur.NORMAL
                        )
                    }
                }
                
                // 绘制阴影(偏移)
                drawContext.canvas.nativeCanvas.apply {
                    translate(10f, 10f)
                    drawRoundRect(
                        0f, 0f, size.width - 20f, size.height - 20f,
                        20f, 20f,
                        paint.asFrameworkPaint()
                    )
                }
            }
            .background(
                Color.White,
                RoundedCornerShape(16.dp)
            ),
        contentAlignment = Alignment.Center
    ) {
        Text("Custom Blur Shadow")
    }
}

/**
 * 内阴影效果
 */
@Composable
private fun InnerShadow() {
    Box(
        modifier = Modifier
            .size(150.dp)
            .drawWithContent {
                drawContent()
                
                // 绘制内阴影
                drawRect(
                    brush = Brush.verticalGradient(
                        colors = listOf(
                            Color.Black.copy(alpha = 0.2f),
                            Color.Transparent,
                            Color.Transparent,
                            Color.Black.copy(alpha = 0.2f)
                        )
                    ),
                    blendMode = BlendMode.DstIn
                )
            }
            .background(
                Color(0xFFF5F5F5),
                RoundedCornerShape(16.dp)
            )
            .border(
                width = 1.dp,
                color = Color.LightGray,
                shape = RoundedCornerShape(16.dp)
            ),
        contentAlignment = Alignment.Center
    ) {
        Text("Inner Shadow")
    }
}

/**
 * 多层阴影效果
 */
@Composable
private fun MultiLayerShadow() {
    Box(
        modifier = Modifier
            .size(150.dp)
            .drawBehind {
                val frameworkPaint = Paint().asFrameworkPaint()
                
                // 第一层 - 大范围浅阴影
                frameworkPaint.apply {
                    color = android.graphics.Color.parseColor("#1A000000")
                    maskFilter = BlurMaskFilter(60f, BlurMaskFilter.Blur.NORMAL)
                }
                drawContext.canvas.nativeCanvas.drawRoundRect(
                    -10f, -10f, size.width + 10f, size.height + 10f,
                    30f, 30f, frameworkPaint
                )
                
                // 第二层 - 中等范围阴影
                frameworkPaint.apply {
                    color = android.graphics.Color.parseColor("#26000000")
                    maskFilter = BlurMaskFilter(30f, BlurMaskFilter.Blur.NORMAL)
                }
                drawContext.canvas.nativeCanvas.drawRoundRect(
                    0f, 0f, size.width, size.height,
                    20f, 20f, frameworkPaint
                )
                
                // 第三层 - 小范围深阴影
                frameworkPaint.apply {
                    color = android.graphics.Color.parseColor("#33000000")
                    maskFilter = BlurMaskFilter(10f, BlurMaskFilter.Blur.NORMAL)
                }
                drawContext.canvas.nativeCanvas.drawRoundRect(
                    5f, 5f, size.width - 5f, size.height - 5f,
                    16f, 16f, frameworkPaint
                )
            }
            .background(
                Color.White,
                RoundedCornerShape(16.dp)
            ),
        contentAlignment = Alignment.Center
    ) {
        Text("Multi-Layer Shadow")
    }
}

/**
 * 渐变阴影效果
 */
@Composable
private fun GradientShadow() {
    Box(
        modifier = Modifier
            .size(150.dp)
            .drawBehind {
                // 创建渐变阴影路径
                val shadowPath = Path().apply {
                    addRoundRect(
                        RoundRect(
                            rect = Rect(
                                offset = Offset(8f, 8f),
                                size = Size(size.width - 16f, size.height - 16f)
                            ),
                            cornerRadius = CornerRadius(20f, 20f)
                        )
                    )
                }
                
                // 绘制渐变阴影
                drawPath(
                    path = shadowPath,
                    brush = Brush.radialGradient(
                        colors = listOf(
                            Color(0xFF6200EE).copy(alpha = 0.4f),
                            Color(0xFF6200EE).copy(alpha = 0.1f),
                            Color.Transparent
                        ),
                        center = Offset(size.width / 2, size.height / 2),
                        radius = size.width * 0.7f
                    )
                )
            }
            .background(
                Color.White,
                RoundedCornerShape(16.dp)
            ),
        contentAlignment = Alignment.Center
    ) {
        Text("Gradient Shadow")
    }
}

4.2 复杂形状阴影实现

@Composable
fun ComplexShapeShadows() {
    Column(
        modifier = Modifier.padding(16.dp),
        verticalArrangement = Arrangement.spacedBy(24.dp)
    ) {
        // 气泡对话框阴影
        ChatBubbleShadow()
        
        // 不规则图形阴影
        IrregularShapeShadow()
        
        // 文字阴影
        TextShadow()
    }
}

/**
 * 聊天气泡带阴影
 */
@Composable
private fun ChatBubbleShadow() {
    val bubbleShape = GenericShape { size, _ ->
        val cornerRadius = 20f
        val triangleHeight = 30f
        val triangleWidth = 40f
        
        // 主体圆角矩形
        moveTo(cornerRadius, 0f)
        lineTo(size.width - cornerRadius, 0f)
        arcTo(
            rect = Rect(
                offset = Offset(size.width - cornerRadius * 2, 0f),
                size = Size(cornerRadius * 2, cornerRadius * 2)
            ),
            startAngleDegrees = 270f,
            sweepAngleDegrees = 90f,
            forceMoveTo = false
        )
        lineTo(size.width, size.height - cornerRadius - triangleHeight)
        arcTo(
            rect = Rect(
                offset = Offset(size.width - cornerRadius * 2, size.height - cornerRadius * 2 - triangleHeight),
                size = Size(cornerRadius * 2, cornerRadius * 2)
            ),
            startAngleDegrees = 0f,
            sweepAngleDegrees = 90f,
            forceMoveTo = false
        )
        
        // 底部三角形
        lineTo(size.width / 2 + triangleWidth / 2, size.height - triangleHeight)
        lineTo(size.width / 2, size.height)
        lineTo(size.width / 2 - triangleWidth / 2, size.height - triangleHeight)
        
        lineTo(cornerRadius, size.height - triangleHeight)
        arcTo(
            rect = Rect(
                offset = Offset(0f, size.height - cornerRadius * 2 - triangleHeight),
                size = Size(cornerRadius * 2, cornerRadius * 2)
            ),
            startAngleDegrees = 90f,
            sweepAngleDegrees = 90f,
            forceMoveTo = false
        )
        lineTo(0f, cornerRadius)
        arcTo(
            rect = Rect(
                offset = Offset(0f, 0f),
                size = Size(cornerRadius * 2, cornerRadius * 2)
            ),
            startAngleDegrees = 180f,
            sweepAngleDegrees = 90f,
            forceMoveTo = false
        )
        close()
    }
    
    Box(
        modifier = Modifier
            .width(200.dp)
            .height(100.dp)
            .shadow(
                elevation = 8.dp,
                shape = bubbleShape,
                clip = false
            )
            .background(Color(0xFF6200EE), bubbleShape)
            .padding(16.dp),
        contentAlignment = Alignment.Center
    ) {
        Text(
            text = "Chat Bubble",
            color = Color.White,
            fontSize = 16.sp
        )
    }
}

/**
 * 不规则图形阴影
 */
@Composable
private fun IrregularShapeShadow() {
    val irregularShape = GenericShape { size, _ ->
        val path = Path().apply {
            // 创建波浪形路径
            moveTo(0f, size.height * 0.3f)
            
            // 上边波浪
            cubicTo(
                size.width * 0.3f, 0f,
                size.width * 0.7f, size.height * 0.6f,
                size.width, size.height * 0.3f
            )
            
            // 右边
            lineTo(size.width, size.height * 0.8f)
            
            // 下边波浪
            cubicTo(
                size.width * 0.7f, size.height,
                size.width * 0.3f, size.height * 0.4f,
                0f, size.height * 0.8f
            )
            
            close()
        }
        addPath(path)
    }
    
    Box(
        modifier = Modifier
            .size(150.dp)
            .shadow(
                elevation = 12.dp,
                shape = irregularShape,
                clip = false,
                spotColor = Color(0xFF00BCD4).copy(alpha = 0.5f),
                ambientColor = Color(0xFF00BCD4).copy(alpha = 0.3f)
            )
            .background(
                brush = Brush.linearGradient(
                    colors = listOf(Color(0xFF00BCD4), Color(0xFF00838F))
                ),
                shape = irregularShape
            )
    )
}

/**
 * 文字阴影效果
 */
@Composable
private fun TextShadow() {
    Column(
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.spacedBy(16.dp)
    ) {
        // 基础文字阴影
        Text(
            text = "Shadow Text",
            fontSize = 32.sp,
            fontWeight = FontWeight.Bold,
            color = Color.White,
            style = TextStyle(
                shadow = Shadow(
                    color = Color.Black.copy(alpha = 0.5f),
                    offset = Offset(4f, 4f),
                    blurRadius = 8f
                )
            )
        )
        
        // 彩色文字阴影
        Text(
            text = "Colorful Shadow",
            fontSize = 32.sp,
            fontWeight = FontWeight.Bold,
            color = Color(0xFF6200EE),
            style = TextStyle(
                shadow = Shadow(
                    color = Color(0xFF00BCD4).copy(alpha = 0.6f),
                    offset = Offset(6f, 6f),
                    blurRadius = 12f
                )
            )
        )
        
        // 多层文字阴影
        Text(
            text = "Multi Shadow",
            fontSize = 32.sp,
            fontWeight = FontWeight.Bold,
            color = Color.White,
            modifier = Modifier.drawBehind {
                val textPaint = Paint().asFrameworkPaint().apply {
                    isAntiAlias = true
                    textSize = 32.sp.toPx()
                    typeface = Typeface.DEFAULT_BOLD
                }
                
                // 绘制多层阴影
                for (i in 5 downTo 1) {
                    textPaint.color = android.graphics.Color.argb(
                        30, 0, 0, 0
                    )
                    drawContext.canvas.nativeCanvas.drawText(
                        "Multi Shadow",
                        i * 2f,
                        size.height / 2 + i * 2f,
                        textPaint
                    )
                }
            }
        )
    }
}

4.3 阴影动画效果

@Composable
fun AnimatedShadows() {
    Column(
        modifier = Modifier.padding(16.dp),
        verticalArrangement = Arrangement.spacedBy(32.dp)
    ) {
        // 呼吸阴影动画
        BreathingShadow()
        
        // 颜色变化阴影
        ColorChangingShadow()
        
        // 偏移动画阴影
        OffsetAnimatingShadow()
        
        // 按压反馈阴影
        PressableShadow()
    }
}

/**
 * 呼吸阴影动画
 */
@Composable
private fun BreathingShadow() {
    val infiniteTransition = rememberInfiniteTransition(label = "breathing")
    
    val elevation by infiniteTransition.animateFloat(
        initialValue = 4.dp.value,
        targetValue = 20.dp.value,
        animationSpec = infiniteRepeatable(
            animation = tween(1500, easing = FastOutSlowInEasing),
            repeatMode = RepeatMode.Reverse
        ),
        label = "elevation"
    )
    
    Box(
        modifier = Modifier
            .size(120.dp)
            .shadow(
                elevation = elevation.dp,
                shape = RoundedCornerShape(16.dp),
                spotColor = Color(0xFF6200EE).copy(alpha = 0.5f)
            )
            .background(Color.White, RoundedCornerShape(16.dp)),
        contentAlignment = Alignment.Center
    ) {
        Text("Breathing")
    }
}

/**
 * 颜色变化阴影
 */
@Composable
private fun ColorChangingShadow() {
    val infiniteTransition = rememberInfiniteTransition(label = "color")
    
    val hue by infiniteTransition.animateFloat(
        initialValue = 0f,
        targetValue = 360f,
        animationSpec = infiniteRepeatable(
            animation = tween(3000, easing = LinearEasing)
        ),
        label = "hue"
    )
    
    val shadowColor = remember(hue) {
        Color.hsl(hue, 0.8f, 0.5f)
    }
    
    Box(
        modifier = Modifier
            .size(120.dp)
            .shadow(
                elevation = 16.dp,
                shape = CircleShape,
                spotColor = shadowColor.copy(alpha = 0.6f),
                ambientColor = shadowColor.copy(alpha = 0.4f)
            )
            .background(Color.White, CircleShape),
        contentAlignment = Alignment.Center
    ) {
        Text("Color Change")
    }
}

/**
 * 偏移动画阴影
 */
@Composable
private fun OffsetAnimatingShadow() {
    val infiniteTransition = rememberInfiniteTransition(label = "offset")
    
    val offsetX by infiniteTransition.animateFloat(
        initialValue = -10f,
        targetValue = 10f,
        animationSpec = infiniteRepeatable(
            animation = tween(2000, easing = FastOutSlowInEasing),
            repeatMode = RepeatMode.Reverse
        ),
        label = "offsetX"
    )
    
    val offsetY by infiniteTransition.animateFloat(
        initialValue = -10f,
        targetValue = 10f,
        animationSpec = infiniteRepeatable(
            animation = tween(2000, easing = FastOutSlowInEasing),
            repeatMode = RepeatMode.Reverse
        ),
        label = "offsetY"
    )
    
    // 使用自定义绘制实现偏移动画
    Box(
        modifier = Modifier
            .size(120.dp)
            .drawBehind {
                val paint = Paint().asFrameworkPaint().apply {
                    color = android.graphics.Color.parseColor("#4D000000")
                    maskFilter = BlurMaskFilter(20f, BlurMaskFilter.Blur.NORMAL)
                }
                
                drawContext.canvas.nativeCanvas.drawRoundRect(
                    offsetX + 10f,
                    offsetY + 10f,
                    size.width + offsetX - 10f,
                    size.height + offsetY - 10f,
                    20f, 20f,
                    paint
                )
            }
            .background(Color.White, RoundedCornerShape(16.dp)),
        contentAlignment = Alignment.Center
    ) {
        Text("Moving Shadow")
    }
}

/**
 * 按压反馈阴影
 */
@Composable
private fun PressableShadow() {
    var isPressed by remember { mutableStateOf(false) }
    
    val elevation by animateDpAsState(
        targetValue = if (isPressed) 4.dp else 16.dp,
        animationSpec = spring(
            dampingRatio = Spring.DampingRatioMediumBouncy,
            stiffness = Spring.StiffnessLow
        ),
        label = "pressElevation"
    )
    
    val scale by animateFloatAsState(
        targetValue = if (isPressed) 0.95f else 1f,
        animationSpec = spring(
            dampingRatio = Spring.DampingRatioMediumBouncy,
            stiffness = Spring.StiffnessLow
        ),
        label = "pressScale"
    )
    
    Box(
        modifier = Modifier
            .size(120.dp)
            .graphicsLayer {
                scaleX = scale
                scaleY = scale
            }
            .shadow(
                elevation = elevation,
                shape = RoundedCornerShape(20.dp),
                spotColor = Color(0xFF6200EE).copy(alpha = 0.5f)
            )
            .background(
                brush = Brush.linearGradient(
                    colors = listOf(Color(0xFF667eea), Color(0xFF764ba2))
                ),
                shape = RoundedCornerShape(20.dp)
            )
            .clickable(
                interactionSource = remember { MutableInteractionSource() },
                indication = null
            ) {
                isPressed = !isPressed
            },
        contentAlignment = Alignment.Center
    ) {
        Text(
            text = if (isPressed) "Pressed" else "Press Me",
            color = Color.White,
            fontWeight = FontWeight.Bold
        )
    }
}

五、最佳实践与性能优化

5.1 性能考量

5.1.1 阴影渲染成本
// 性能对比说明

// ✅ 推荐:使用系统shadow modifier(GPU加速)
Box(
    modifier = Modifier.shadow(elevation = 8.dp)
)

// ⚠️ 谨慎:自定义BlurMaskFilter(CPU渲染,较耗性能)
Box(
    modifier = Modifier.drawBehind {
        val paint = Paint().asFrameworkPaint().apply {
            maskFilter = BlurMaskFilter(30f, BlurMaskFilter.Blur.NORMAL)
        }
        // 绘制逻辑...
    }
)

// ❌ 避免:频繁创建Paint对象
Box(
    modifier = Modifier.drawBehind {
        repeat(100) {
            val paint = Paint()  // 每次绘制都创建新对象
            // 绘制逻辑...
        }
    }
)
5.1.2 优化建议
// ✅ 缓存Paint对象
object ShadowPaints {
    val defaultShadowPaint = Paint().asFrameworkPaint().apply {
        maskFilter = BlurMaskFilter(20f, BlurMaskFilter.Blur.NORMAL)
    }
}

// ✅ 使用drawWithCache缓存复杂阴影
@Composable
fun OptimizedShadowBox() {
    Box(
        modifier = Modifier
            .size(100.dp)
            .drawWithCache {
                // 在缓存中准备阴影绘制
                val shadowPath = Path().apply {
                    addRoundRect(
                        RoundRect(
                            rect = size.toRect(),
                            cornerRadius = CornerRadius(20f, 20f)
                        )
                    )
                }
                
                onDrawBehind {
                    // 使用缓存的路径绘制阴影
                    drawPath(
                        path = shadowPath,
                        color = Color.Black.copy(alpha = 0.2f)
                    )
                }
            }
            .background(Color.White, RoundedCornerShape(20.dp))
    )
}

5.2 常见问题与解决方案

问题原因解决方案
阴影被裁剪clip参数为true或父布局裁剪设置clip = false,检查父布局Modifier
阴影颜色不显示API版本不支持检查Compose版本(1.2+支持彩色阴影)
阴影闪烁频繁重组重建shadow使用remember缓存shadow配置
自定义形状阴影变形shape路径复杂简化路径,使用GenericShape
性能下降过多阴影或复杂模糊减少阴影数量,降低elevation值

5.3 跨版本兼容性处理

@Composable
fun CompatibleShadowBox(
    elevation: Dp,
    modifier: Modifier = Modifier,
    shape: Shape = RectangleShape,
    ambientColor: Color = Color.Black,
    spotColor: Color = Color.Black
) {
    val hasColorSupport = Build.VERSION.SDK_INT >= Build.VERSION_CODES.P ||
            androidx.compose.ui.platform.LocalContext.current
                .packageManager
                .hasSystemFeature("android.software.compose")
    
    Box(
        modifier = if (hasColorSupport) {
            modifier.shadow(
                elevation = elevation,
                shape = shape,
                ambientColor = ambientColor,
                spotColor = spotColor
            )
        } else {
            // 降级方案:使用默认黑色阴影
            modifier.shadow(
                elevation = elevation,
                shape = shape
            )
        }
    )
}

5.4 Material Design 3阴影规范

@Composable
fun Material3ShadowTokens() {
    Column(
        modifier = Modifier.padding(16.dp),
        verticalArrangement = Arrangement.spacedBy(16.dp)
    ) {
        // Level 0 - 无阴影
        ShadowTokenBox(
            elevation = 0.dp,
            label = "Level 0"
        )
        
        // Level 1 - 组件静止状态
        ShadowTokenBox(
            elevation = 1.dp,
            label = "Level 1"
        )
        
        // Level 2 - 卡片静止状态
        ShadowTokenBox(
            elevation = 3.dp,
            label = "Level 2"
        )
        
        // Level 3 - 抬升状态
        ShadowTokenBox(
            elevation = 6.dp,
            label = "Level 3"
        )
        
        // Level 4 - 浮动按钮
        ShadowTokenBox(
            elevation = 8.dp,
            label = "Level 4 (FAB)"
        )
        
        // Level 5 - 对话框
        ShadowTokenBox(
            elevation = 12.dp,
            label = "Level 5 (Dialog)"
        )
    }
}

@Composable
private fun ShadowTokenBox(elevation: Dp, label: String) {
    Row(
        verticalAlignment = Alignment.CenterVertically,
        horizontalArrangement = Arrangement.spacedBy(16.dp)
    ) {
        Surface(
            modifier = Modifier.size(80.dp),
            shape = RoundedCornerShape(12.dp),
            shadowElevation = elevation,
            color = MaterialTheme.colorScheme.surface
        ) {}
        
        Text(
            text = label,
            style = MaterialTheme.typography.bodyMedium
        )
    }
}

5.5 实用工具类封装

/**
 * 阴影工具类
 */
object ShadowUtils {
    
    /**
     * 创建Material风格阴影
     */
    @Composable
    fun Modifier.materialShadow(
        elevation: Dp,
        shape: Shape = RoundedCornerShape(8.dp)
    ): Modifier = this.shadow(
        elevation = elevation,
        shape = shape,
        ambientColor = Color.Black.copy(alpha = 0.1f),
        spotColor = Color.Black.copy(alpha = 0.2f)
    )
    
    /**
     * 创建霓虹灯风格阴影
     */
    @Composable
    fun Modifier.neonShadow(
        color: Color,
        elevation: Dp = 8.dp
    ): Modifier = this.shadow(
        elevation = elevation,
        shape = RoundedCornerShape(12.dp),
        ambientColor = color.copy(alpha = 0.3f),
        spotColor = color.copy(alpha = 0.5f)
    )
    
    /**
     * 创建柔和阴影
     */
    @Composable
    fun Modifier.softShadow(
        elevation: Dp = 4.dp,
        shape: Shape = RoundedCornerShape(16.dp)
    ): Modifier = this.shadow(
        elevation = elevation,
        shape = shape,
        ambientColor = Color.Black.copy(alpha = 0.05f),
        spotColor = Color.Black.copy(alpha = 0.1f)
    )
    
    /**
     * 创建深度阴影(适合深色主题)
     */
    @Composable
    fun Modifier.deepShadow(
        elevation: Dp = 16.dp,
        shape: Shape = RoundedCornerShape(8.dp)
    ): Modifier = this.shadow(
        elevation = elevation,
        shape = shape,
        ambientColor = Color.Black.copy(alpha = 0.4f),
        spotColor = Color.Black.copy(alpha = 0.6f)
    )
}

// 使用示例
@Composable
fun ShadowUtilsDemo() {
    Column(
        modifier = Modifier.padding(16.dp),
        verticalArrangement = Arrangement.spacedBy(16.dp)
    ) {
        // Material阴影
        Box(
            modifier = Modifier
                .size(100.dp)
                .ShadowUtils.materialShadow(elevation = 4.dp)
                .background(Color.White)
        )
        
        // 霓虹阴影
        Box(
            modifier = Modifier
                .size(100.dp)
                .ShadowUtils.neonShadow(color = Color(0xFF00E5FF))
                .background(Color(0xFF1A1A2E))
        )
        
        // 柔和阴影
        Box(
            modifier = Modifier
                .size(100.dp)
                .ShadowUtils.softShadow()
                .background(Color.White)
        )
        
        // 深度阴影
        Box(
            modifier = Modifier
                .size(100.dp)
                .ShadowUtils.deepShadow()
                .background(Color(0xFF2D2D2D))
        )
    }
}

总结

Jetpack Compose提供了强大而灵活的阴影系统,从简单的shadow modifier到复杂的自定义绘制,开发者可以根据需求选择合适的方式:

  1. 基础使用:使用Modifier.shadow()快速添加阴影效果
  2. 颜色定制:利用ambientColorspotColor实现彩色阴影
  3. 自定义形状:通过GenericShape创建任意形状的阴影
  4. 动画效果:配合Compose动画API实现动态阴影
  5. 性能优化:合理使用缓存和GPU加速,避免过度绘制

掌握这些技术,您可以在Compose应用中创建出丰富、美观且具有层次感的UI效果。


参考资源