Pixel 3设备GLSurfaceView竖屏圆角异常及全圆角实现咨询
我之前调试Pixel系列设备的GLSurfaceView圆角适配时,刚好碰到过一模一样的情况——系统默认只给顶部状态栏对应的区域应用了圆角裁剪,但底部导航栏对应的GLSurfaceView区域没被处理。下面分享两种经过验证的解决方案,都是热门3D游戏常用的思路:
方案1:利用Android View的Outline裁剪(简单易实现)
SurfaceView(包括GLSurfaceView)的Surface默认独立于Window的Surface,所以普通的setClipToOutline(true)可能不生效,需要先让GLSurfaceView的Surface和Window的Insets对齐:
开启GLSurfaceView的窗口Insets适配
在你的GLSurfaceView初始化代码里添加:glSurfaceView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN); glSurfaceView.setOnApplyWindowInsetsListener((v, insets) -> { // 获取系统提供的圆角半径(用Insets更适配不同设备) WindowInsetsCompat insetsCompat = WindowInsetsCompat.toWindowInsetsCompat(insets); DisplayCutoutCompat cutout = insetsCompat.getDisplayCutout(); if (cutout != null) { // Pixel3上下圆角半径一致,取底部对应的值 float cornerRadius = cutout.getCornerRadii()[2]; // 创建完整圆角的Outline Outline outline = new Outline(); outline.setRoundRect(0, 0, v.getWidth(), v.getHeight(), cornerRadius); v.setOutline(outline); v.setClipToOutline(true); } return insets.consumeSystemWindowInsets(); });核心是通过
DisplayCutout获取系统原生圆角半径,给GLSurfaceView设置覆盖四边的圆角Outline,再开启裁剪。注意事项
- 确保Activity是全屏模式(或布局延伸到状态栏、导航栏下方),否则无法获取正确的Insets信息
- 部分Android版本需要在布局文件的GLSurfaceView标签中添加
android:clipToOutline="true"
方案2:OpenGL渲染层手动绘制圆角遮罩(游戏常用,更灵活)
如果方案1在某些场景下不生效(比如OpenGL渲染内容需要突破View裁剪限制),可以在OpenGL渲染流程中手动添加圆角遮罩层:
准备圆角遮罩的Shader
创建片段Shader实现圆角裁剪逻辑:precision mediump float; varying vec2 v_texCoord; uniform vec2 u_resolution; uniform float u_radius; void main() { vec2 pos = gl_FragCoord.xy / u_resolution; vec2 center = vec2(0.5, 0.5); vec2 dist = abs(pos - center); float circleDist = length(dist); // 计算圆角区域的透明度,实现遮罩效果 float alpha = smoothstep(u_radius / u_resolution.x, (u_radius + 1.0) / u_resolution.x, circleDist); gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0 - alpha); }这个Shader会在屏幕四个角绘制透明圆角区域,遮挡GLSurfaceView的角落。
在渲染流程中添加遮罩绘制
在onDrawFrame方法的最后,绘制覆盖全屏的矩形并应用遮罩Shader:// 假设已加载并链接好遮罩Shader程序 int maskProgram = ...; glUseProgram(maskProgram); // 传入屏幕分辨率和圆角半径(Pixel3的圆角半径约为18dp,转成像素值) glUniform2f(glGetUniformLocation(maskProgram, "u_resolution"), screenWidth, screenHeight); glUniform1f(glGetUniformLocation(maskProgram, "u_radius"), dpToPx(18)); // 绘制全屏矩形 glBindBuffer(GL_ARRAY_BUFFER, fullScreenVbo); glEnableVertexAttribArray(0); glVertexAttribPointer(0, 2, GL_FLOAT, false, 0, 0); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); glDisableVertexAttribArray(0);这种方式完全由OpenGL控制,不受View层级限制,是热门3D游戏的主流方案。
为什么热门游戏能实现全圆角?
大部分热门游戏会选择方案2,因为他们的渲染逻辑完全在OpenGL层,通过遮罩可以精准控制圆角显示,同时避免View层级的兼容性问题;部分游戏也会结合方案1,利用系统Insets信息动态适配不同设备的圆角半径。
内容的提问来源于stack exchange,提问作者Xavier

