针对OpenClaw项目的刘海屏适配,这里提供完整的解决方案:

检测刘海屏设备
// 刘海屏检测工具类
class NotchUtils {
public:
static bool hasNotch() {
#if defined(__ANDROID__)
return detectAndroidNotch();
#elif defined(__APPLE__)
return detectiOSNotch();
#else
return false;
#endif
}
static void getSafeAreaInsets(int& top, int& bottom, int& left, int& right) {
// 获取安全区域
#if defined(__ANDROID__)
getAndroidSafeArea(top, bottom, left, right);
#elif defined(__APPLE__)
getiOSSafeArea(top, bottom, left, right);
#else
top = bottom = left = right = 0;
#endif
}
};
Android平台适配
1 AndroidManifest.xml配置
<!-- 全屏显示,内容延伸到刘海区域 -->
<meta-data
android:name="android.notch_support"
android:value="true" />
<!-- 设置窗口延伸到刘海区域 -->
<meta-data
android:name="android.max_aspect"
android:value="2.1" />
2 代码适配
// Android JNI接口#include <jni.h>
#include <android/log.h>
#include <android/native_window_jni.h>
extern "C" {
JNIEXPORT jboolean JNICALL
Java_com_yourgame_OpenClawActivity_hasDisplayCutout(JNIEnv* env, jobject thiz) {
jclass activityClass = env->GetObjectClass(thiz);
jmethodID getWindowMethod = env->GetMethodID(activityClass, "getWindow", "()Landroid/view/Window;");
jobject window = env->CallObjectMethod(thiz, getWindowMethod);
jclass windowClass = env->GetObjectClass(window);
jmethodID getDecorViewMethod = env->GetMethodID(windowClass, "getDecorView", "()Landroid/view/View;");
jobject decorView = env->CallObjectMethod(window, getDecorViewMethod);
jclass viewClass = env->GetObjectClass(decorView);
jmethodID getRootWindowInsetsMethod = env->GetMethodID(viewClass, "getRootWindowInsets", "()Landroid/view/WindowInsets;");
jobject windowInsets = env->CallObjectMethod(decorView, getRootWindowInsetsMethod);
if (windowInsets != nullptr) {
jclass windowInsetsClass = env->GetObjectClass(windowInsets);
jmethodID getDisplayCutoutMethod = env->GetMethodID(windowInsetsClass, "getDisplayCutout", "()Landroid/view/DisplayCutout;");
jobject displayCutout = env->CallObjectMethod(windowInsets, getDisplayCutoutMethod);
return (displayCutout != nullptr) ? JNI_TRUE : JNI_FALSE;
}
return JNI_FALSE;
}
}
#endif
iOS平台适配
1 Info.plist配置
<!-- 扩展内容到刘海区域 -->
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>UIStatusBarHidden</key>
<false/>
<!-- iPhone X系列适配 -->
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UISupportedInterfaceOrientations~iphone</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
2 iOS代码适配
// iOS刘海屏检测
- (BOOL)hasNotch {
if (@available(iOS 11.0, *)) {
UIWindow *window = [[UIApplication sharedApplication] keyWindow];
return window.safeAreaInsets.top > 20;
}
return NO;
}
- (UIEdgeInsets)safeAreaInsets {
if (@available(iOS 11.0, *)) {
return [[UIApplication sharedApplication] keyWindow].safeAreaInsets;
}
return UIEdgeInsetsZero;
}
OpenGL渲染适配
class NotchAdaptiveRenderer {
private:
int notchTop = 0;
int notchBottom = 0;
int notchLeft = 0;
int notchRight = 0;
public:
void updateSafeArea() {
NotchUtils::getSafeAreaInsets(notchTop, notchBottom, notchLeft, notchRight);
}
// 调整视口,避开刘海区域
void adjustViewport(int screenWidth, int screenHeight) {
int viewportX = notchLeft;
int viewportY = notchBottom; // OpenGL坐标原点在左下角
int viewportWidth = screenWidth - notchLeft - notchRight;
int viewportHeight = screenHeight - notchTop - notchBottom;
glViewport(viewportX, viewportY, viewportWidth, viewportHeight);
}
// 获取游戏内容区域
Rect getContentRect(int screenWidth, int screenHeight) {
return Rect(
notchLeft,
notchBottom,
screenWidth - notchLeft - notchRight,
screenHeight - notchTop - notchBottom
);
}
// 判断点是否在安全区域内
bool isPointInSafeArea(int x, int y, int screenWidth, int screenHeight) {
return (x >= notchLeft &&
x <= screenWidth - notchRight &&
y >= notchBottom &&
y <= screenHeight - notchTop);
}
};
UI布局策略
class NotchAwareUILayout {
public:
enum LayoutMode {
LAYOUT_FULLSCREEN, // 全屏,内容可能被刘海遮挡
LAYOUT_SAFE_AREA, // 只在安全区域内显示
LAYOUT_ADAPTIVE // 自适应,重要内容避开刘海
};
struct UISafeZones {
Rect topSafeZone; // 顶部安全区域(通常用于状态栏)
Rect contentSafeZone; // 主要内容区域
Rect bottomSafeZone; // 底部安全区域(用于虚拟按键)
};
static UISafeZones calculateSafeZones(int width, int height,
int notchTop, int notchBottom,
int notchLeft, int notchRight) {
UISafeZones zones;
// 顶部安全区域(状态栏高度)
zones.topSafeZone = Rect(0, height - notchTop, width, notchTop);
// 主要内容区域(避开刘海)
zones.contentSafeZone = Rect(
notchLeft,
notchBottom,
width - notchLeft - notchRight,
height - notchTop - notchBottom
);
// 底部安全区域(虚拟按键区域)
zones.bottomSafeZone = Rect(0, 0, width, notchBottom);
return zones;
}
// 放置重要UI元素到安全区域
static void positionCriticalUI(Rect& uiRect, const UISafeZones& safeZones) {
// 确保UI元素完全在内容安全区域内
if (!safeZones.contentSafeZone.contains(uiRect)) {
uiRect = uiRect.intersection(safeZones.contentSafeZone);
}
}
};
游戏特定适配
// OpenClaw游戏特定适配
class OpenClawNotchAdapter {
private:
CameraController* camera;
HUD* hud;
public:
void adaptForNotch() {
auto screenSize = getScreenSize();
auto safeArea = NotchUtils::getSafeArea();
// 1. 调整相机视角
if (camera) {
float aspectRatio = (screenSize.width - safeArea.left - safeArea.right)
/ (screenSize.height - safeArea.top - safeArea.bottom);
camera->setAspectRatio(aspectRatio);
// 避免重要游戏元素被刘海遮挡
camera->setViewportMargins(
safeArea.left,
safeArea.bottom,
safeArea.right,
safeArea.top
);
}
// 2. 调整HUD布局
if (hud) {
// 分数、生命值等放在顶部安全区域下方
hud->setPosition(HUD::POSITION_SCORE,
screenSize.width / 2,
screenSize.height - safeArea.top - 30);
// 操作按钮放在底部安全区域上方
hud->setPosition(HUD::POSITION_JUMP_BUTTON,
screenSize.width - safeArea.right - 80,
safeArea.bottom + 80);
// 虚拟摇杆避开左侧刘海
hud->setPosition(HUD::POSITION_JOYSTICK,
safeArea.left + 100,
safeArea.bottom + 100);
}
// 3. 调整游戏物品生成区域
ItemSpawner::setSpawnBounds(
safeArea.left + 50,
safeArea.bottom + 50,
screenSize.width - safeArea.right - 50,
screenSize.height - safeArea.top - 50
);
}
// 处理不同方向的刘海
void handleOrientationChange(Orientation newOrientation) {
updateSafeArea();
adaptForNotch();
// 横屏时可能需要特殊处理
if (newOrientation == ORIENTATION_LANDSCAPE) {
// 横屏时,刘海通常在左侧或右侧
// 调整UI布局
adjustHUDForLandscape();
}
}
};
测试方案
// 刘海屏适配测试
class NotchAdapterTest {
public:
static void runTests() {
testSafeAreaCalculation();
testUIPlacement();
testGameplayAdaptation();
}
static void testSafeAreaCalculation() {
// 模拟不同刘海配置
vector<NotchConfig> testConfigs = {
{44, 0, 0, 0}, // iPhone X竖屏
{0, 0, 44, 44}, // 横屏
{30, 30, 0, 0}, // 对称刘海
{0, 50, 0, 0}, // 只有下巴
};
for (auto& config : testConfigs) {
auto safeZones = NotchAwareUILayout::calculateSafeZones(
1080, 2340, config.top, config.bottom, config.left, config.right
);
assert(safeZones.contentSafeZone.width > 0);
assert(safeZones.contentSafeZone.height > 0);
}
}
};
配置选项
// 刘海屏适配配置
struct NotchSettings {
bool enableNotchAdaptation = true;
LayoutMode layoutMode = LAYOUT_ADAPTIVE;
// 是否允许内容延伸到刘海区域
bool extendIntoNotch = false;
// 刘海区域是否显示特殊效果
bool notchAreaEffects = true;
// 刘海区域透明度(0.0-1.0)
float notchAreaAlpha = 0.3f;
// 用户可调整的设置
void saveSettings() {
// 保存到配置文件
}
void loadSettings() {
// 从配置文件加载
}
};
最佳实践建议
分级**:
- 关键UI元素(按钮、生命值)必须放在安全区域
- 次要元素(装饰、背景)可以延伸到刘海区域
- 游戏核心区域避开刘海
-
动态适配:
- 支持横竖屏切换
- 支持不同刘海形状(圆形、药丸形、不规则形)
- 支持旋转时刘海位置变化
-
用户体验:
- 提供适配开关
- 保持游戏核心体验一致
- 避免刘海区域遮挡重要信息
这个解决方案覆盖了从底层检测到上层UI适配的完整流程,可以根据OpenClaw的具体需求进行调整。
版权声明:除非特别标注,否则均为本站原创文章,转载时请以链接形式注明文章出处。