【UI篇11】蒙层适配实现方案

67 阅读3分钟

1. 概述

1.1 背景

蒙层是提升用户界面视觉效果和交互体验的关键元素。蒙层主要用于:

  • 提升内容可读性:在图片上叠加文字时,通过渐变蒙层增强文字对比度
  • 视觉层次感:区分前景内容和背景图片
  • 品牌视觉统一:通过主题色蒙层实现品牌色调统一
  • 交互反馈:模糊蒙层用于弹窗背景,增强焦点感

1.2 技术架构

蒙层体系
├── 渐变蒙层(GradientMask)
│   ├── 底部渐变蒙层
│   ├── 顶部渐变蒙层
│   └── 多段渐变蒙层
├── 高斯模糊蒙层(BlurMask)
│   ├── 实时模糊
│   └── 区域模糊
└── 纯色遮罩
    ├── 半透明遮罩
    └── 交互遮罩

2. 蒙层类型与实现

2.1 渐变蒙层(GradientMask)

2.1.1 底部渐变蒙层

使用场景:底部功能区,提升文字和图标可读性

实现原理

/**
 * 底部蒙层生成流程
 * 1. 提取图片主色调(Palette API)
 * 2. HSV颜色空间转换
 * 3. 生成多段渐变 Drawable
 * 4. 动态设置蒙层高度
 */// 2. 转换HSB参数到HSV色彩空间(Android中HSV的V对应亮度)
val hsv = FloatArray(3).apply {
    // 从主色中获取原始HSV,再替换为目标H/S/V
    Color.colorToHSV(dominantColor, this)
    this[0] = hue.coerceIn(0f, 360f)               // 色相限制在0-360
    this[1] = (saturation / 100f).coerceIn(0f, 1f) // 饱和度转换为0-1范围
    this[2] = (brightness / 100f).coerceIn(0f, 1f) // 亮度转换为0-1范围
}
​
// 3. 计算渐变的起始和结束颜色(处理Alpha值)
val startColor = Color.HSVToColor(
    (startAlpha * 255).roundToInt().coerceIn(0, 255),
    hsv
)
val endColor = Color.HSVToColor(
    (endAlpha * 255).roundToInt().coerceIn(0, 255),
    hsv
)
​
// 4. 创建并配置渐变Drawable
return GradientDrawable(orientation, intArrayOf(startColor, endColor)).apply {
    // 可根据需求添加其他配置,如圆角
    if (radiusPx != -1) {
        cornerRadius = radiusPx.toFloat() // 示例:添加圆角(需实现dp转px)
    }
}
位置透明度(Alpha)说明
顶部(0%)0 (0x00)完全透明
中间(50%)128 (0x80)半透明
底部(100%)192 (0xC0)较强遮盖

2.1.2 顶部渐变蒙层

使用场景:顶部导航栏区域,避免图片内容干扰状态栏图标

实现特点

  • 高度:120dp(固定)
  • 方向:从上到下渐变(TOP_BOTTOM)
  • 颜色:通常为黑色到透明

2.1.3 多段渐变蒙层

使用场景:需要更平滑的渐变过渡

设计规范

梯度位置     不透明度
0%          0%      (完全透明)
21%         10%     (微弱遮盖)
57%         35%     (中等遮盖)
78%         55%     (较强遮盖)
100%        70%     (强遮盖)
override fun onBoundsChange(bounds: android.graphics.Rect) {
    super.onBoundsChange(bounds)
    // 从上到下的渐变
    gradient = LinearGradient(
        0f,
        bounds.top.toFloat(),
        0f,
        bounds.bottom.toFloat(),
        colors,
        positions,
        Shader.TileMode.CLAMP
    )
    paint.shader = gradient
​
    // 创建只在下半部分有圆角的路径,使用 addRoundRect 与图片圆角实现保持一致
    val left = bounds.left.toFloat()
    val top = bounds.top.toFloat()
    val right = bounds.right.toFloat()
    val bottom = bounds.bottom.toFloat()
​
    path.reset()
    rectF.set(left, top, right, bottom)
​
    // 使用 addRoundRect 创建圆角路径,与 RoundImageView 的实现方式保持一致
    // rids 格式:左上x, 左上y, 右上x, 右上y, 右下x, 右下y, 左下x, 左下y
    val rids = floatArrayOf(
        0f, 0f,  // 左上角(直角)
        0f, 0f,  // 右上角(直角)
        cornerRadius, cornerRadius,  // 右下角(圆角)
        cornerRadius, cornerRadius   // 左下角(圆角)
    )
    path.addRoundRect(rectF, rids, Path.Direction.CW)
​
    invalidateSelf()
}

2.2 高斯模糊蒙层(BlurMask)

2.2.1 实时高斯模糊

使用场景:弹窗背景

// 适配 Android 12+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
    // 1. 创建高斯模糊效果:radius(模糊半径),越大约模糊但性能越低(建议≤25)
    val blurEffect = RenderEffect.createBlurEffect(
        15f, // 水平模糊半径
        15f, // 垂直模糊半径
        Shader.TileMode.CLAMP // 边界模式(CLAMP=重复边缘像素,避免黑边)
    )
    // 2. 给目标 View 设置模糊(如布局、ImageView、TextView)
    targetView.setRenderEffect(blurEffect)
    
    // 可选:模糊 + 颜色叠加(避免模糊后过亮/过暗)
    val colorFilter = ColorFilter.createBlendModeFilter(
        Color.argb(128, 0, 0, 0), // 半透明黑色叠加
        BlendMode.SRC_ATOP
    )
    targetView.setColorFilter(colorFilter)
}

2.2.2 区域高斯模糊

使用场景:特定区域模糊

2.3 纯色遮罩(SolidMask)

2.3.1 半透明遮罩

使用场景:弹窗背景、引导蒙层

实现方式

// 方式1:View 背景
val maskView = View(context)
maskView.setBackgroundColor(0x80000000)  // 50% 透明黑色// 方式2:Drawable
val colorDrawable = ColorDrawable(0x80000000)
imageView.foreground = colorDrawable

2.3.2 交互遮罩