You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在VideoView上实现圆形GLSurfaceView?

解决GLSurfaceView叠加在VideoView上无法显示圆形的问题

我之前遇到过类似的场景,这里帮你分析问题根源并给出几个可行的解决方案:

问题本质

你当前用dispatchDraw+clipPath的方式,其实对GLSurfaceView完全不起作用——因为GLSurfaceView的渲染逻辑和普通View天差地别:它拥有独立的Surface和OpenGL渲染线程,所有GL绘制内容都是直接输出到这个独立Surface上的,和View体系的Canvas没有关联。之前叠加在ImageView上看起来是圆形,大概率是视觉巧合或者系统临时合成表现,并不是真正裁剪了GLSurfaceView的内容。而VideoView本身也是SurfaceView子类,两个Surface叠加时,系统不会再应用View层的Canvas裁剪,所以方形就露出来了。

解决方案

方案1:在OpenGL渲染层面做圆形裁剪(最可靠)

直接在GLRenderer的绘制逻辑里加入圆形裁剪,这是从根源解决问题的方式,因为我们直接控制了GL画面的输出。可以用**模板测试(Stencil Test)**实现:

  1. 先绘制一个圆形到模板缓冲
  2. 设置只渲染模板缓冲标记为1的区域
  3. 再绘制摄像头画面
  4. 最后关闭模板测试

示例代码修改你的Renderer:

public class CameraGLRenderer implements GLSurfaceView.Renderer {
    // 省略其他成员变量和初始化逻辑...

    @Override
    public void onDrawFrame(GL10 gl) {
        // 启用模板测试
        gl.glEnable(GL10.GL_STENCIL_TEST);
        // 清除模板缓冲
        gl.glClear(GL10.GL_STENCIL_BUFFER_BIT);
        
        // 设置模板规则:绘制时将模板值设为1
        gl.glStencilFunc(GL10.GL_ALWAYS, 1, 0xFF);
        gl.glStencilOp(GL10.GL_REPLACE, GL10.GL_REPLACE, GL10.GL_REPLACE);

        // 绘制圆形模板(用三角形扇形绘制覆盖整个视图的圆)
        drawCircleMask(gl);

        // 更新模板规则:只绘制模板值等于1的区域
        gl.glStencilFunc(GL10.GL_EQUAL, 1, 0xFF);
        gl.glStencilOp(GL10.GL_KEEP, GL10.GL_KEEP, GL10.GL_KEEP);

        // 绘制你的摄像头画面逻辑
        drawCameraFrame(gl);

        // 关闭模板测试
        gl.glDisable(GL10.GL_STENCIL_TEST);
    }

    private void drawCircleMask(GL10 gl) {
        gl.glColor4f(1.0f, 1.0f, 1.0f, 1.0f); // 颜色不影响模板,随便设置即可
        gl.glBegin(GL10.GL_TRIANGLE_FAN);
        gl.glVertex2f(0, 0); // 圆心(归一化坐标,对应视图中心)
        int segments = 36; // 分段数越多圆越平滑
        for (int i = 0; i <= segments; i++) {
            float angle = (float) (i * 2 * Math.PI / segments);
            float x = (float) Math.cos(angle);
            float y = (float) Math.sin(angle);
            gl.glVertex2f(x, y);
        }
        gl.glEnd();
    }

    // 省略drawCameraFrame等其他方法...
}

方案2:使用ViewOutlineProvider(Android 5.0+ 简单易用)

如果你的最低兼容版本是Android 5.0(API 21)及以上,可以用系统提供的ViewOutlineProvider实现裁剪,代码非常简洁:

修改你的SampleGLView的onSizeChanged方法:

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);
    int radius = Math.min(w, h) / 2;
    
    // 设置OutlineProvider
    setOutlineProvider(new ViewOutlineProvider() {
        @Override
        public void getOutline(View view, Outline outline) {
            // 设置圆形轮廓,用setRoundRect实现(宽高相等时就是圆形)
            outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), radius);
        }
    });
    // 开启裁剪到轮廓
    setClipToOutline(true);
}

注意:部分旧机型可能对SurfaceView子类的ClipToOutline支持不佳,建议测试后使用。

方案3:添加遮罩层(兼容性最好)

如果以上两种方法都不适用,最简单的兼容方案是在GLSurfaceView上方叠加一个遮罩View,挡住方形区域只露出圆形:

  1. 创建圆形遮罩的Drawable(res/drawable/circle_mask.xml):
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <!-- 周围区域的颜色,用半透明黑色模拟遮罩 -->
    <solid android:color="#CC000000" />
    <item>
        <!-- 中间透明的圆形 -->
        <shape android:shape="oval">
            <solid android:color="@android:color/transparent" />
        </shape>
    </item>
</shape>
  1. 修改布局,在GLSurfaceView上方加一个相同大小的遮罩View:
<FrameLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <VideoView
        android:id="@+id/videoView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
    <com.yourpackage.SampleGLView
        android:id="@+id/glView"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:layout_gravity="center" />
    <!-- 遮罩层 -->
    <View
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:layout_gravity="center"
        android:background="@drawable/circle_mask" />
</FrameLayout>

这个方法不需要修改任何GL或View代码,兼容性覆盖所有Android版本,缺点是遮罩层会挡住GLSurfaceView的触摸事件,你可以在遮罩View里重写onTouchEvent,返回false或者直接转发给GLSurfaceView。

内容的提问来源于stack exchange,提问作者jeel raja

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 09:37:50