如何为圆角半透明矩形Drawable添加边缘渐隐效果?
实现Drawable边缘渐隐的柔和效果
嘿,这个需求很常见,我给你两种实用方案,你可以根据自己的场景选择:
方案1:用XML LayerList快速实现(无需代码)
这种方法适合静态场景,直接通过XML组合多个Drawable层就能搞定,不需要写Java/Kotlin代码。
假设你原来的Drawable是一个带圆角的半透明黑矩形,我们可以在它上面叠加四个边缘的渐变层,让边缘从透明过渡到半透明黑,实现渐隐效果:
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <!-- 底层:你的原有半透明黑圆角矩形 --> <item> <shape> <solid android:color="#80000000" /> <!-- 半透明黑色,alpha=128 --> <corners android:radius="16dp" /> <!-- 圆角大小,和你的原有设置保持一致 --> </shape> </item> <!-- 顶部渐隐层:高度16dp,从透明过渡到半透明黑 --> <item> <shape> <gradient android:type="linear" android:angle="270" <!-- 从上到下渐变 --> android:startColor="#00000000" <!-- 顶部完全透明 --> android:endColor="#80000000" /> <!-- 渐变到底部的半透明黑 --> <corners android:topLeftRadius="16dp" android:topRightRadius="16dp" /> <!-- 和底层圆角匹配 --> </shape> <padding android:top="16dp" /> <!-- 只显示顶部16dp的渐变区域 --> </item> <!-- 底部渐隐层:高度16dp,从透明过渡到半透明黑 --> <item> <shape> <gradient android:type="linear" android:angle="90" <!-- 从下到上渐变 --> android:startColor="#00000000" <!-- 底部完全透明 --> android:endColor="#80000000" /> <!-- 渐变到顶部的半透明黑 --> <corners android:bottomLeftRadius="16dp" android:bottomRightRadius="16dp" /> </shape> <padding android:bottom="16dp" /> </item> <!-- 左侧渐隐层:宽度16dp,从透明过渡到半透明黑 --> <item> <shape> <gradient android:type="linear" android:angle="0" <!-- 从左到右渐变 --> android:startColor="#00000000" <!-- 左侧完全透明 --> android:endColor="#80000000" /> <!-- 渐变到右侧的半透明黑 --> <corners android:topLeftRadius="16dp" android:bottomLeftRadius="16dp" /> </shape> <padding android:left="16dp" /> </item> <!-- 右侧渐隐层:宽度16dp,从透明过渡到半透明黑 --> <item> <shape> <gradient android:type="linear" android:angle="180" <!-- 从右到左渐变 --> android:startColor="#00000000" <!-- 右侧完全透明 --> android:endColor="#80000000" /> <!-- 渐变到左侧的半透明黑 --> <corners android:topRightRadius="16dp" android:bottomRightRadius="16dp" /> </shape> <padding android:right="16dp" /> </item> </layer-list>
你可以调整16dp这个值来控制渐隐的范围,值越大,渐变过渡越平缓。
方案2:自定义Drawable(更灵活可控)
如果需要动态调整渐隐大小、圆角或者颜色,或者要处理更复杂的渐变逻辑,自定义Drawable会更合适。下面是一个Kotlin实现的例子:
import android.graphics.* import android.graphics.drawable.Drawable import android.util.TypedValue import android.content.res.Resources class FadingEdgeDrawable @JvmOverloads constructor( private val cornerRadius: Float = dpToPx(16f), private val fadeSize: Float = dpToPx(16f), private val bgColor: Int = Color.argb(128, 0, 0, 0) ) : Drawable() { private val paint = Paint(Paint.ANTI_ALIAS_FLAG) private val rectF = RectF() override fun onBoundsChange(bounds: Rect) { super.onBoundsChange(bounds) rectF.set(bounds) } override fun draw(canvas: Canvas) { // 1. 绘制底层圆角矩形 paint.color = bgColor canvas.drawRoundRect(rectF, cornerRadius, cornerRadius, paint) // 2. 绘制顶部渐隐层 paint.shader = LinearGradient( rectF.left, rectF.top, rectF.left, rectF.top + fadeSize, Color.TRANSPARENT, bgColor, Shader.TileMode.CLAMP ) val topRect = RectF(rectF.left, rectF.top, rectF.right, rectF.top + fadeSize) canvas.drawRoundRect(topRect, cornerRadius, cornerRadius, paint) // 3. 绘制底部渐隐层 paint.shader = LinearGradient( rectF.left, rectF.bottom, rectF.left, rectF.bottom - fadeSize, Color.TRANSPARENT, bgColor, Shader.TileMode.CLAMP ) val bottomRect = RectF(rectF.left, rectF.bottom - fadeSize, rectF.right, rectF.bottom) canvas.drawRoundRect(bottomRect, cornerRadius, cornerRadius, paint) // 4. 绘制左侧渐隐层 paint.shader = LinearGradient( rectF.left, rectF.top, rectF.left + fadeSize, rectF.top, Color.TRANSPARENT, bgColor, Shader.TileMode.CLAMP ) val leftRect = RectF(rectF.left, rectF.top, rectF.left + fadeSize, rectF.bottom) canvas.drawRoundRect(leftRect, cornerRadius, cornerRadius, paint) // 5. 绘制右侧渐隐层 paint.shader = LinearGradient( rectF.right, rectF.top, rectF.right - fadeSize, rectF.top, Color.TRANSPARENT, bgColor, Shader.TileMode.CLAMP ) val rightRect = RectF(rectF.right - fadeSize, rectF.top, rectF.right, rectF.bottom) canvas.drawRoundRect(rightRect, cornerRadius, cornerRadius, paint) // 重置Shader,避免影响后续绘制 paint.shader = null } override fun setAlpha(alpha: Int) { paint.alpha = alpha invalidateSelf() } override fun setColorFilter(colorFilter: ColorFilter?) { paint.colorFilter = colorFilter invalidateSelf() } override fun getOpacity(): Int { return PixelFormat.TRANSLUCENT } private fun dpToPx(dp: Float): Float { return TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, dp, Resources.getSystem().displayMetrics ) } }
使用的时候直接创建实例并设置给View即可:
val fadingDrawable = FadingEdgeDrawable( cornerRadius = dpToPx(16f), fadeSize = dpToPx(16f), bgColor = Color.argb(128, 0, 0, 0) ) yourView.background = fadingDrawable // 辅助方法:dp转px private fun dpToPx(dp: Float): Float { return TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, dp, resources.displayMetrics ) }
总结
- 如果是静态UI,XML方案足够简单高效,不需要写代码;
- 如果需要动态调整参数或者更复杂的效果,自定义Drawable能提供更强的灵活性。
内容的提问来源于stack exchange,提问作者Paradox
相关产品推荐
相关产品推荐

