libGDX 与 Android 15 的边缘到边缘(edge-to-edge)强制规范
嘿,刚好我之前踩过这个坑,给你捋捋怎么在libGDX里搞定状态栏、导航栏和刘海的尺寸位置适配,完美符合Google的边缘到-edge要求~
核心思路:借助Android原生代码拿系统UI信息
libGDX是跨平台框架,本身没直接提供获取Android系统UI的API,所以得在Android端的AndroidLauncher里通过原生代码拿到这些数据,再传递给libGDX的游戏逻辑里用。
1. 先开启边缘到-edge模式
首先得在AndroidLauncher的onCreate里打开这个模式,让应用可以延伸到系统UI的区域:
getWindow().setDecorFitsSystemWindows(false);
2. 获取状态栏的高度
状态栏高度可以通过Android的资源ID获取,兼容大部分旧版本:
int statusBarHeight = 0; int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android"); if (resourceId > 0) { statusBarHeight = getResources().getDimensionPixelSize(resourceId); }
3. 获取导航栏的尺寸与位置
导航栏可能在底部或侧边(比如平板),所以用WindowInsets获取上下左右的内边距更准确(Android 10+推荐),旧版本可以 fallback 到资源ID获取高度:
int navBarLeft = 0, navBarTop = 0, navBarRight = 0, navBarBottom = 0; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { WindowInsets insets = getWindow().getDecorView().getRootWindowInsets(); if (insets != null) { Insets navInsets = insets.getInsets(WindowInsets.Type.navigationBars()); navBarLeft = navInsets.left; navBarTop = navInsets.top; navBarRight = navInsets.right; navBarBottom = navInsets.bottom; } } else { int navBarId = getResources().getIdentifier("navigation_bar_height", "dimen", "android"); if (navBarId > 0) { navBarBottom = getResources().getDimensionPixelSize(navBarId); } }
4. 处理刘海(Cutouts)区域
首先要在AndroidManifest.xml里设置允许应用延伸到刘海区域:
<meta-data android:name="android.window.layoutInDisplayCutoutMode" android:value="shortEdges" />
然后在AndroidLauncher里获取刘海的安全区域和具体矩形位置:
int safeLeft = 0, safeTop = 0, safeRight = 0, safeBottom = 0; List<Rect> cutoutRects = new ArrayList<>(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { DisplayCutout cutout = getWindow().getDecorView().getRootWindowInsets().getDisplayCutout(); if (cutout != null) { safeLeft = cutout.getSafeInsetLeft(); safeTop = cutout.getSafeInsetTop(); safeRight = cutout.getSafeInsetRight(); safeBottom = cutout.getSafeInsetBottom(); cutoutRects = cutout.getBoundingRects(); // 所有刘海的矩形区域 } }
5. 把数据传递给libGDX游戏
把上面拿到的所有参数传给你的libGDX游戏实例,比如:
MyGame game = new MyGame(statusBarHeight, navBarLeft, navBarTop, navBarRight, navBarBottom, safeLeft, safeTop, safeRight, safeBottom, cutoutRects); initialize(game, config);
这里要注意:libGDX的坐标原点是左下角,而Android是左上角,所以转换的时候要做调整,比如状态栏的底部在libGDX里的Y坐标是Gdx.graphics.getHeight() - statusBarHeight,安全区域的顶部Y坐标是Gdx.graphics.getHeight() - safeTop。
6. 监听屏幕旋转,动态更新数据
屏幕旋转后,系统UI的位置尺寸会变化,所以要在AndroidLauncher里重写onConfigurationChanged方法,重新获取数据并通知游戏更新:
@Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); // 重复上面的获取逻辑,拿到新的参数 // ... // 通知游戏更新,要确保在GL线程执行 ((MyGame) getApplicationListener()).updateSystemUIInsets(/* 新参数 */); }
然后在MyGame里写对应的更新方法:
public void updateSystemUIInsets(/* 所有参数 */) { Gdx.app.post(() -> { // 更新类内的变量 // 触发UI重新布局 }); }
最后小提示
你可以用这些数据调整UI布局:比如把按钮、文本等重要内容放在安全区域内,背景可以延伸到系统UI和刘海区域,既满足edge-to-edge的要求,又不会让内容被遮挡。
内容来源于stack exchange

