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

为什么 SurfaceView 难实现圆角?
SurfaceView 作为 Android 传统的视频渲染视图,由于其特殊的双缓冲机制和独立绘制层,导致在实现圆角时会遇到几个棘手问题:
- 黑边问题:SurfaceView 的内容是在独立图层渲染的,视图裁剪只影响控件本身,不影响其内容
- 锯齿明显:常规的 setCornerRadius 方法在 SurfaceView 上几乎无效
- 性能瓶颈:频繁重绘时容易出现画面闪烁
TextureView 的救赎
与 SurfaceView 不同,TextureView 可以完美融入视图层级,支持透明度变化和变形效果。实现圆角的核心方案是:
- 自定义 RoundCornerTextureView 继承 TextureView
- 使用 Outline 裁剪视图边界
- 通过 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
}
常见问题解决方案
- RecyclerView 复用问题:
- 在 onViewRecycled 时手动释放 TextureView
-
使用 setSurfaceTextureListener 确保纹理正确绑定
-
Android 10+ 硬件加速限制:
- 在 manifest 中添加 android:hardwareAccelerated="true"
- 避免在同一个窗口中使用多个 TextureView
进阶:动态圆角动画
想要实现动态变化的圆角效果,可以结合 ValueAnimator:
ValueAnimator.ofFloat(0f, 20f).apply {
duration = 500
addUpdateListener { animator ->
roundCornerTextureView.setCornerRadius(animator.animatedValue as Float)
}
start()
}
总结
通过 TextureView + 自定义着色器的方案,我们不仅解决了圆角问题,还获得了更好的视觉效果和灵活性。虽然 TextureView 相比 SurfaceView 有轻微的性能损失,但在大多数场景下都是值得的。
更多推荐


所有评论(0)