如何在Android中实现与Figma一致的Drop Shadow效果?
核心思路
Figma的多层阴影本质是叠加多个独立阴影层,每个层包含X偏移、Y偏移、模糊半径、扩散半径、颜色(含透明度)。Android原生的CardView/Button默认阴影无法满足自定义需求,需要通过叠加自定义阴影Drawable或使用官方Material组件实现。
方案1:自定义ShadowDrawable + LayerDrawable叠加
通过自定义Drawable实现单个阴影的所有参数控制,再用LayerDrawable将多层阴影与原组件背景叠加,完全匹配Figma参数。
自定义ShadowDrawable代码
class ShadowDrawable : Drawable() { private val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply { style = Paint.Style.FILL } private val shadowBounds = RectF() // 对应Figma阴影参数 var shadowX: Float = 0f var shadowY: Float = 0f var shadowBlur: Float = 0f var shadowSpread: Float = 0f var shadowColor: Int = Color.BLACK override fun draw(canvas: Canvas) { // 设置阴影参数:模糊半径、X/Y偏移、颜色 paint.setShadowLayer(shadowBlur, shadowX, shadowY, shadowColor) // 用inset模拟扩散值:正数缩小区域,负数扩大区域(对应Figma的spread) shadowBounds.set(bounds) shadowBounds.inset(-shadowSpread, -shadowSpread) // 绘制圆角矩形(适配Button/CardView的圆角,可根据需求修改) canvas.drawRoundRect(shadowBounds, bounds.width() / 2f, bounds.height() / 2f, paint) } override fun setAlpha(alpha: Int) { paint.alpha = alpha } override fun setColorFilter(colorFilter: ColorFilter?) { paint.colorFilter = colorFilter } override fun getOpacity(): Int = PixelFormat.TRANSLUCENT }
给Button添加多层阴影
fun setupMultiShadowButton(button: Button) { // 保留原按钮背景 val originalBackground = button.background // 第一层阴影(完全对应Figma参数) val shadow1 = ShadowDrawable().apply { shadowX = 2f // Figma X阴影值 shadowY = 4f // Figma Y阴影值 shadowBlur = 8f // Figma模糊度 shadowSpread = 2f // Figma扩散度 shadowColor = Color.parseColor("#33000000") // Figma阴影颜色(注意ARGB顺序) } // 第二层阴影 val shadow2 = ShadowDrawable().apply { shadowX = 0f shadowY = 2f shadowBlur = 12f shadowSpread = 0f shadowColor = Color.parseColor("#1A000000") } // 叠加阴影层与原背景 val layers = arrayOf(shadow2, shadow1, originalBackground) val layerDrawable = LayerDrawable(layers) // 设置层间距,避免阴影被组件边界裁剪 layerDrawable.setLayerInset(0, 16, 16, 16, 16) layerDrawable.setLayerInset(1, 8, 8, 8, 8) button.background = layerDrawable // 开启硬件加速,保证阴影模糊效果正确 button.setLayerType(View.LAYER_TYPE_HARDWARE, null) }
方案2:使用MaterialShapeDrawable(官方Material组件库)
如果项目已引入com.google.android.material:material库,可直接用MaterialShapeDrawable实现阴影,稳定性优于第三方库。
给CardView添加多层阴影
fun setupCardViewMultiShadow(cardView: CardView) { // 关闭CardView原生阴影 cardView.cardElevation = 0f val context = cardView.context // 定义圆角(匹配Figma组件圆角) val shapeModel = ShapeAppearanceModel.builder() .setAllCornerSizes(16f) .build() // 第一层阴影 val shadow1 = MaterialShapeDrawable(shapeModel).apply { setShadowColor(Color.parseColor("#33000000")) shadowCompatibilityMode = MaterialShapeDrawable.SHADOW_COMPAT_MODE_ALWAYS elevation = shadowBlur // 用elevation模拟模糊半径 translateX = shadowX translateY = shadowY } // 第二层阴影 val shadow2 = MaterialShapeDrawable(shapeModel).apply { setShadowColor(Color.parseColor("#1A000000")) shadowCompatibilityMode = MaterialShapeDrawable.SHADOW_COMPAT_MODE_ALWAYS elevation = 12f translateX = 0f translateY = 2f } // CardView背景层 val cardBackground = MaterialShapeDrawable(shapeModel).apply { fillColor = ContextCompat.getColorStateList(context, R.color.white) } // 叠加层并设置给CardView val layers = arrayOf(shadow2, shadow1, cardBackground) val layerDrawable = LayerDrawable(layers) layerDrawable.setLayerInset(0, 16, 16, 16, 16) layerDrawable.setLayerInset(1, 8, 8, 8, 8) cardView.background = layerDrawable }
关键注意事项
- 硬件加速:必须开启
setLayerType(View.LAYER_TYPE_HARDWARE, null),否则阴影模糊效果会失真,尤其是大模糊值场景。 - 颜色格式:Figma的阴影颜色是RGBA,Android是ARGB,转换时要调整顺序(比如Figma的
#00000033对应Android的#33000000)。 - 扩散值模拟:Android的
setShadowLayer没有直接的spread参数,通过RectF.inset(-spread, -spread)扩大绘制区域来模拟,和Figma的spread逻辑完全一致。 - 边界裁剪:叠加阴影层时要设置
setLayerInset给阴影留出空间,避免被组件边界裁剪。
内容的提问来源于stack exchange,提问作者Vinit Hingu
相关产品推荐
相关产品推荐

