起因:Iphone X掀起的一波刘海屏手机潮(虽然个人觉得很丑),国内各大手机厂商纷纷效仿,导致我们的小游戏各种黑边奇丑无比
翻阅CSDN大神奉献的博文参考: android 兼容所有刘海屏的方案大全
踩坑历程一: 项目经理要求不适配了,简单处理,让游戏界面显示状态栏。
1.判断手机是否为刘海屏手机,根据 android 兼容所有刘海屏的方案大全 可以获取华为、小米、VO的刘海屏标志。
2.刘海屏手机下:
a.启动Activity保持全屏不变
b.游戏Activity,如果为竖屏状态,需要退出全屏,显示状态栏(退出全屏),并且隐藏虚拟按键,如果为横屏状态:
c.横竖屏切换时游戏Activity需要全屏,并且GLSurfaceView的大小改变,并偏移。
竖屏->横屏 屏幕全屏,GLSurfaceView宽度= 屏幕宽度-状态栏高度, GLSurfaceView高度= 屏幕高度
横屏->竖屏 退出屏幕全屏,GLSurfaceView宽度= 屏幕宽度, GLSurfaceView高度= 屏幕高度-状态栏高度
d.第三方支付SDK初始化时自动弹出登入Activity,导致状态栏被迫显示,返回游戏Activity需要重新设置。
………..N种情况,不在详细列举
3.游戏场景横竖屏切换,需要操作两个步骤
a.调用Activity的强制旋转。
b.游戏内glview需要重新设置FrameSize() auto glview = Director::getInstance()->getOpenGLView();
涉及代码:
判断刘海屏情况
package com.pdragon.common.utils;
import java.lang.reflect.Method;
import com.pdragon.common.UserApp;
import com.pdragon.common.UserSystemProperties;
import android.content.Context;
/**
* 全面屏
* @author Administrator
*
*/
@SuppressWarnings("rawtypes")
public class NotchInScreen {
public final static String TAG = "NotchInScreen";
//屏幕类型
public static enum ScreenType{
LIUHAI, //刘海屏
LOUKONG, //镂空屏
};
public int top_height = 0; //顶部高度
public int top_width = 0; //顶部隐藏部分的宽度
public int bottom_height = 0; //底部高度
public int bottom_width = 0; //底部隐藏部分的宽度
public boolean hasNotchInScreen = false;
public boolean needSetGamePadding = false;//是否需要设置游戏Layout的padding,由于华为和小米在Manifes当中设置当前App是否需要绘制全面屏。所以这个类里面的的代码需要做相应的调整
private static NotchInScreen instance = null;
public static synchronized NotchInScreen getInstance(Context ctx){
if(instance== null){
synchronized (NotchInScreen.class) {
if(instance== null){
instance = new NotchInScreen(ctx);
}
}
}
return instance;
}
public NotchInScreen(Context ctx){
int[] ret = new int[] {0, 0, 0, 0};
if(hasNotchInScreenHuawei(ctx)){
hasNotchInScreen = true;
needSetGamePadding = false;
ret = getNotchSizeHuawei(ctx);
}
else if(hasNotchInScreenOppo(ctx)){
hasNotchInScreen = true;
needSetGamePadding = true;
ret = getNotchSizeOppo(ctx);
}
else if(hasNotchInScreenVivo(ctx)){
hasNotchInScreen = true;
needSetGamePadding = false;
ret = getNotchSizeVivo(ctx);
}
else if(hasNotchInScreenXiaomi(ctx)){
hasNotchInScreen = true;
needSetGamePadding = false;
ret = getNotchSizeXiaomi(ctx);
}
if(hasNotchInScreen){
top_width = ret[0];
top_height = ret[1];
bottom_width = ret[2];
bottom_height = ret[3];
}
UserApp.LogD(String.format("needSetGamePadding = %b, hasNotchInScreen=%b, {%d, %d, %d, %d}", needSetGamePadding, hasNotchInScreen, top_width, top_height, bottom_width, bottom_height));
}
/**
* 判断是否是刘海屏
*/
@SuppressWarnings("finally")
public static boolean hasNotchInScreenHuawei(Context ctx) {
boolean ret = false;
try {
ClassLoader cl = ctx.getClassLoader();
Class HwNotchSizeUtil = cl.loadClass("com.huawei.android.util.HwNotchSizeUtil");
Method get = HwNotchSizeUtil.getMethod("hasNotchInScreen");
ret = TypeUtil.ObjectToBooleanDef(get.invoke(HwNotchSizeUtil, null), false);
} catch (Exception e) {
UserApp.LogD(TAG, "huawei hasNotchInScreen Exception");
} finally {
UserApp.LogD(TAG, "huawei hasNotchInScreen:" + ret);
return ret;
}
}
// 获取刘海的高宽
@SuppressWarnings("finally")
public static int[] getNotchSizeHuawei(Context ctx) {
int[] ret = new int[] {0, 0, 0, 0};
int[] ret_huawei = new int[]{0, 0};
try {
ClassLoader cl = ctx.getClassLoader();
Class<?> HwNotchSizeUtil = cl.loadClass("com.huawei.android.util.HwNotchSizeUtil");
Method get = HwNotchSizeUtil.getMethod("getNotchSize");
ret_huawei = (int[]) get.invoke(HwNotchSizeUtil);
ret[0] = ret_huawei[0];
ret[1] = ret_huawei[1];
} catch (Exception e) {
UserApp.LogD(TAG, "huawei getNotchSize Exception");
} finally {
return ret;
}
}
//判断是否是刘海屏
public static boolean hasNotchInScreenOppo(Context ctx) {
boolean ret = ctx.getPackageManager().hasSystemFeature("com.oppo.feature.screen.heteromorphism");
//UserApp.LogD(TAG, "Oppo hasNotchInScreenHuawei:" + ret);
return ret;
}
// 获取刘海的高宽
public static int[] getNotchSizeOppo(Context ctx) {
int[] ret = new int[] {324, 80, 0, 0};
return ret;
}
public static final int NOTCH_IN_SCREEN_VOIO=0x00000020;//是否有凹槽
public static final int ROUNDED_IN_SCREEN_VOIO=0x00000008;//是否有圆角
//判断是否是刘海屏
public static boolean hasNotchInScreenVivo(Context ctx) {
boolean ret = false;
try {
ClassLoader cl = ctx.getClassLoader();
Class FtFeature = cl.loadClass("android.util.FtFeature");
Method get = FtFeature.getMethod("isFeatureSupport", int.class);
ret = Boolean.parseBoolean(TypeUtil.ObjectToString(get.invoke(FtFeature, NOTCH_IN_SCREEN_VOIO)));
}catch (Exception e) {
UserApp.LogD(TAG, "vivo hasNotchInScreen Exception");
} finally {
UserApp.LogD(TAG, "vivo hasNotchInScreen:" + ret);
return ret;
}
}
// 获取刘海的高宽
public static int[] getNotchSizeVivo(Context ctx) {
int height = CommonUtil.dip2px(ctx, 50);
int width = CommonUtil.getScreenWidth(ctx);
int[] ret = new int[] {width, height, width, height};
return ret;
}
//判断是否是刘海屏
public static boolean hasNotchInScreenXiaomi(Context ctx) {
if(UserSystemProperties.getInt("ro.miui.notch", 0) == 1){
return true;
}
return false;
}
// 获取刘海的高宽
public static int[] getNotchSizeXiaomi(Context ctx) {
int status_bar_height = 0;
int resourceId = ctx.getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
status_bar_height = ctx.getResources().getDimensionPixelSize(resourceId);
}
int[] ret = new int[] {0, status_bar_height, 0, 0};
return ret;
}
}
/**
* 隐藏虚拟系统按键
*/
@TargetApi(Build.VERSION_CODES.KITKAT)
public static void hideVirtualNavigation(Activity act) {
if(!UserApp.curApp().isGameApp()){
return;//应用类App不隐藏虚拟按键
}
if (Build.VERSION.SDK_INT >= 19) {
if (Build.VERSION.SDK_INT >= 21) {
act.getWindow().setNavigationBarColor(Color.TRANSPARENT);
}
NotchInScreen inScreen = NotchInScreen.getInstance(act);
if(inScreen.hasNotchInScreen){//存在凹凸屏
//声明当前屏幕状态的参数并获取
act.getWindow().getDecorView().setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
}
else{//不存在凹凸屏
act.getWindow().getDecorView().setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION // hide nav bar
| View.SYSTEM_UI_FLAG_FULLSCREEN // hide status bar
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
}
}
}
/**
* 设置全屏
*/
public static void setFullScreen(Activity act) {
// 设置全屏的相关属性,获取当前的屏幕状态,然后设置全屏
act.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
// 全屏下的状态码:1098974464
// 窗口下的状态吗:1098973440
}
/**
* 退出全屏
*/
public static void quitFullScreen(Activity act) {
// 声明当前屏幕状态的参数并获取
Window window = act.getWindow();
final WindowManager.LayoutParams attrs = window.getAttributes();
attrs.flags &= (~WindowManager.LayoutParams.FLAG_FULLSCREEN);
window.setAttributes(attrs);
window.clearFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
}
经历以上历程,会发现,很多的情况并非理想中的情况,总有兼容性问题。经过多方测试,最终放弃。
踩坑历程二:游戏内全部适配刘海屏,所有手机均全屏显示:
1.小米需要在Manifest中设置绘制刘海屏
<meta-data android:name="notch.config" android:value="portrait|landscape"/>
2.华为手机需要在Manifest中设置绘制刘海屏
<meta-data android:name="android.notch_support" android:value="true"/>
3.vivo只要设置了全面屏即可,然而,在8.01的设备上,系统内部bug,导致设置了之后,不生效(和vivo的开发人员沟通得出的结果),唯一的途径就是,给vivo的人在服务器设置白名单,只有服务器设置了之后,我们的App才会在进入时绘制刘海屏(此处巨坑)。
全面屏适配:
<meta-data android:name="android.max_aspect" android:value="2.4" />