Android刘海屏适配历程,兼容Android8.0

起因: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" />

 

0 条评论
发表一条评论