限时福利领取


在 Android 视频播放开发中,为播放器添加圆角效果是个常见需求,但很多开发者在使用 ExoPlayer 时都会遇到黑边、锯齿等问题。今天我们就来深入探讨如何完美实现圆角效果。

圆角播放器效果图

为什么 SurfaceView 难实现圆角?

SurfaceView 作为 Android 传统的视频渲染视图,由于其特殊的双缓冲机制和独立绘制层,导致在实现圆角时会遇到几个棘手问题:

  • 黑边问题:SurfaceView 的内容是在独立图层渲染的,视图裁剪只影响控件本身,不影响其内容
  • 锯齿明显:常规的 setCornerRadius 方法在 SurfaceView 上几乎无效
  • 性能瓶颈:频繁重绘时容易出现画面闪烁

TextureView 的救赎

与 SurfaceView 不同,TextureView 可以完美融入视图层级,支持透明度变化和变形效果。实现圆角的核心方案是:

  1. 自定义 RoundCornerTextureView 继承 TextureView
  2. 使用 Outline 裁剪视图边界
  3. 通过 GLSL 着色器实现抗锯齿

完整实现代码

以下是 RoundCornerTextureView 的核心实现:

class RoundCornerTextureView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : TextureView(context, attrs, defStyleAttr) {

    private var cornerRadius = 0f

    init {
        outlineProvider = object : ViewOutlineProvider() {
            override fun getOutline(view: View, outline: Outline) {
                outline.setRoundRect(0, 0, width, height, cornerRadius)
            }
        }
        clipToOutline = true
    }

    fun setCornerRadius(radius: Float) {
        cornerRadius = radius
        invalidateOutline()
    }
}

抗锯齿优化方案

为了消除圆角的锯齿感,我们需要在 GLSL 着色器中添加边缘柔化处理:

// 片段着色器代码
precision mediump float;
uniform sampler2D uTexture;
varying vec2 vTextureCoord;
uniform vec2 uSize;
uniform float uRadius;

void main() {
    vec2 uv = vTextureCoord;
    vec2 center = uSize * 0.5;
    vec2 position = uv * uSize - center;

    float dist = length(position) - (uSize.x * 0.5 - uRadius);
    float alpha = 1.0 - smoothstep(0.0, 1.0, dist);

    vec4 color = texture2D(uTexture, uv);
    gl_FragColor = vec4(color.rgb, color.a * alpha);
}

抗锯齿效果对比

性能优化技巧

  • 内存优化:TextureView 比 SurfaceView 多占用约 5-10MB 内存,但现代设备基本可以忽略
  • 动态调整:在 ViewTreeObserver 中监听尺寸变化,避免频繁重绘
view.viewTreeObserver.addOnPreDrawListener {
    // 根据实际尺寸动态计算最佳圆角半径
    val radius = min(width, height) * 0.1f
    setCornerRadius(radius)
    true
}

常见问题解决方案

  1. RecyclerView 复用问题
  2. 在 onViewRecycled 时手动释放 TextureView
  3. 使用 setSurfaceTextureListener 确保纹理正确绑定

  4. Android 10+ 硬件加速限制

  5. 在 manifest 中添加 android:hardwareAccelerated="true"
  6. 避免在同一个窗口中使用多个 TextureView

进阶:动态圆角动画

想要实现动态变化的圆角效果,可以结合 ValueAnimator:

ValueAnimator.ofFloat(0f, 20f).apply {
    duration = 500
    addUpdateListener { animator ->
        roundCornerTextureView.setCornerRadius(animator.animatedValue as Float)
    }
    start()
}

总结

通过 TextureView + 自定义着色器的方案,我们不仅解决了圆角问题,还获得了更好的视觉效果和灵活性。虽然 TextureView 相比 SurfaceView 有轻微的性能损失,但在大多数场景下都是值得的。

Logo

音视频技术社区,一个全球开发者共同探讨、分享、学习音视频技术的平台,加入我们,与全球开发者一起创造更加优秀的音视频产品!

更多推荐