You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android如何实现Figma/XD中带自定义参数的投影效果?

Android实现Figma/XD自定义投影效果方案

问题核心痛点

原生elevation属性仅支持系统默认的固定黑色半透明阴影,无法实现Figma中标注的自定义偏移、模糊半径、带透明度彩色阴影效果;使用selector、layer-list实现的阴影本质是静态渐变叠加,无真实高斯模糊效果,且会被TextInputLayout的内部布局层级覆盖,仅能在EditText上生效。

投影效果参考
Figma属性参考


可用实现方案

方案1:使用官方Material组件库自带能力(推荐,性能最优)

Material Components 1.2.0及以上版本自带的MaterialShapeDrawable原生支持自定义阴影全参数,无需关闭硬件加速,和TextInputLayout同属官方组件库,适配无冲突。
核心实现步骤:

  • 确保项目已引入Material Components依赖
  • 直接获取TextInputLayout的内置背景(本身就是MaterialShapeDrawable实例),修改阴影参数即可,无需额外自定义控件或Drawable

示例代码:

// 扩展方法:dp转px
fun Int.dpToPx(): Float = this * Resources.getSystem().displayMetrics.density

textInputLayout.apply {
    // 关闭默认描边样式,避免和自定义效果冲突
    boxBackgroundMode = TextInputLayout.BOX_BACKGROUND_FILLED
    val shapeBg = background as MaterialShapeDrawable
    // 配置圆角参数匹配设计稿
    shapeBg.shapeAppearanceModel = shapeBg.shapeAppearanceModel.toBuilder()
        .setAllCornerSizes(8.dpToPx())
        .build()
    // 设置控件主体填充色
    shapeBg.fillColor = ColorStateList.valueOf(Color.WHITE)
    // 配置阴影参数,完全对齐Figma标注
    shapeBg.setShadowColor(Color.parseColor("#1A000000")) // 带透明度的阴影色
    shapeBg.shadowRadius = 12.dpToPx() // 模糊半径
    shapeBg.shadowOffsetX = 2.dpToPx() // X轴偏移
    shapeBg.shadowOffsetY = 4.dpToPx() // Y轴偏移
    // 置零原生elevation,避免系统默认阴影叠加
    elevation = 0f
}

方案2:自定义Drawable实现(无第三方依赖,兼容所有API版本)

如果项目未引入Material组件库,可以通过自定义Drawable基于Paint.setShadowLayer()实现阴影效果,全版本兼容。
核心注意点:需要给控件开启软件渲染层,否则阴影在硬件加速模式下可能不显示;不要将阴影设置给TextInputLayout内部的EditText,要直接设置给TextInputLayout本身,同时关闭TextInputLayout默认的box背景。

核心实现代码:

class CustomShadowDrawable(
    private val bgColor: Int,
    private val shadowColor: Int,
    private val shadowRadius: Float,
    private val offsetX: Float,
    private val offsetY: Float,
    private val cornerRadius: Float
) : Drawable() {
    private val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
        color = bgColor
        setShadowLayer(shadowRadius, offsetX, offsetY, shadowColor)
    }

    override fun draw(canvas: Canvas) {
        val drawRect = RectF(
            bounds.left + shadowRadius - offsetX,
            bounds.top + shadowRadius - offsetY,
            bounds.right - shadowRadius - offsetX,
            bounds.bottom - shadowRadius - offsetY
        )
        canvas.drawRoundRect(drawRect, cornerRadius, cornerRadius, paint)
    }

    override fun setAlpha(alpha: Int) {
        paint.alpha = alpha
    }

    override fun setColorFilter(colorFilter: ColorFilter?) {
        paint.colorFilter = colorFilter
    }

    override fun getOpacity(): Int = PixelFormat.TRANSLUCENT
}

使用方式:

val shadowBg = CustomShadowDrawable(
    bgColor = Color.WHITE,
    shadowColor = Color.parseColor("#1A000000"),
    shadowRadius = 12.dpToPx(),
    offsetX = 2.dpToPx(),
    offsetY = 4.dpToPx(),
    cornerRadius = 8.dpToPx()
)
textInputLayout.apply {
    setLayerType(View.LAYER_TYPE_SOFTWARE, null)
    background = shadowBg
    boxBackgroundMode = TextInputLayout.BOX_BACKGROUND_NONE
}

适配注意事项

  • 两种方案都需要给控件四周留出和阴影半径相等的外边距/内边距,避免阴影被父布局边界裁剪
  • 不要同时使用原生elevation和自定义阴影,否则会出现双层阴影叠加的问题
  • 如果需要支持阴影点击事件穿透,自定义Drawable时重写getPadding方法将阴影区域算入控件padding即可

内容的提问来源于stack exchange,提问作者Taimoor Khan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 04:09:16