Android elevation在多圆角矩形下失效,寻求系统识别背景的方案
刚好之前处理过类似的问题,给你几个贴合需求的解决方案,尽量避免手动绘制阴影,同时支持translationZ动画:
解决方案:让系统自动识别自定义圆角背景的elevation阴影
方案1:使用MaterialShapeDrawable(优先推荐)
这是官方Material Components库提供的方案,天生支持和elevation、translationZ动画联动,完全不需要你手动处理阴影渲染。
步骤:
- 确保项目依赖了Material Components库(版本可以根据你的SDK版本调整):
implementation 'com.google.android.material:material:1.9.0' - 在代码中创建并设置
MaterialShapeDrawable作为View的背景:// 定义圆角大小,这里以16dp为例 val cornerRadius = TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, 16f, resources.displayMetrics ) // 创建形状外观模型,设置圆角 val shapeModel = ShapeAppearanceModel.builder() .setAllCornerSizes(cornerRadius) .build() // 创建MaterialShapeDrawable val shapeDrawable = MaterialShapeDrawable(shapeModel) shapeDrawable.fillColor = ContextCompat.getColorStateList(this, R.color.your_background_color) // 同步View的elevation值 shapeDrawable.elevation = view.elevation // 设置为View的背景 view.background = shapeDrawable
这样设置后,系统会自动根据MaterialShapeDrawable的圆角形状生成阴影,而且translationZ动画执行时,阴影会跟着自动变化,完全不需要额外处理。
方案2:让自定义Drawable支持Outline识别
如果你必须使用自己编写的圆角Drawable(比如自定义渐变圆角背景),可以让Drawable重写getOutline方法,配合系统自带的ViewOutlineProvider.BACKGROUND,让系统识别它的形状。
步骤:
- 重写自定义Drawable的
getOutline方法:class CustomRoundedDrawable : Drawable() { private var cornerRadius = 0f // 你的其他绘制逻辑(比如填充颜色、渐变等)... override fun getOutline(outline: Outline) { // 根据Drawable的边界和圆角,设置Outline的形状 outline.setRoundRect(bounds, cornerRadius) } // 提供设置圆角的方法 fun setCornerRadius(radius: Float) { cornerRadius = radius invalidateSelf() } } - 给View设置OutlineProvider:
val customDrawable = CustomRoundedDrawable().apply { setCornerRadius(16.dpToPx()) // 设置其他属性... } view.background = customDrawable // 让系统使用背景Drawable的Outline来生成阴影 view.outlineProvider = ViewOutlineProvider.BACKGROUND // 注意:这里不需要开启clipToOutline,因为我们只是用Outline生成阴影,不是裁剪View view.clipToOutline = false
这样系统就能根据你的自定义Drawable的轮廓生成正确的圆角阴影,translationZ动画也会正常生效。
备选方案:自定义ViewOutlineProvider(尽量不用的退路)
如果上面两个方案都不适用,只能退而求其次使用自定义ViewOutlineProvider+convexPath的方式,但可以优化性能,避免动画期间频繁创建Path:
class RoundedOutlineProvider(private val cornerRadius: Float) : ViewOutlineProvider() { // 缓存Path,避免每次getOutline都重新创建 private val cachedPath = Path() override fun getOutline(view: View, outline: Outline) { val rect = RectF(0f, 0f, view.width.toFloat(), view.height.toFloat()) cachedPath.reset() cachedPath.addRoundRect(rect, cornerRadius, cornerRadius, Path.Direction.CW) outline.setConvexPath(cachedPath) } }
设置给View:
view.outlineProvider = RoundedOutlineProvider(16.dpToPx()) view.clipToOutline = false
这个方案也能支持translationZ动画,系统会自动根据Outline更新阴影,只是需要你自己维护圆角大小和View尺寸的同步。
内容的提问来源于stack exchange,提问作者Re'em
相关产品推荐
相关产品推荐

