由于苹果公司的“先进设计”导致各大手机厂商纷纷跟风其设计,导致Android的屏幕适配出现新的刚需——刘海屏的适配。为了简化这些适配操作以及繁琐的判断封装优化出一个工具库:BangScreenToolsMaster
AndroidP及以上的方式必须适用于sdk大于等于28的情况下使用。
显示模式: Android P中新增了一个布局参数属性 |
|
LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT | 只有当DisplayCutout完全包含在系统栏中时,才允许窗口延伸到DisplayCutout区域。 否则,窗口布局不与DisplayCutout区域重叠 |
LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER | 不使用刘海屏区域 |
LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES | 该窗口始终允许延伸到屏幕短边上的DisplayCutout区域。 |
方法 | 接口描述 |
getBoundingRects() | 返回Rects的列表,每个Rects都是显示屏上非功能区域的边界矩形。 |
getSafeInsetLeft () | 返回安全区域距离屏幕左边的距离,单位是px。 |
getSafeInsetRight () | 返回安全区域距离屏幕右边的距离,单位是px。 |
getSafeInsetTop () | 返回安全区域距离屏幕顶部的距离,单位是px。 |
getSafeInsetBottom() | 返回安全区域距离屏幕底部的距离,单位是px。 |
使用新增的meta-data
属性android.notch_support
。在应用的AndroidManifest.xml
中增加meta-data
属性,此属性不仅可以针对Application
生效,也可以对Activity
配置生效。
如下所示:
Application
生效,意味着该应用的所有页面,系统都不会做竖屏场景的特殊下移或者是横屏场景的右移特殊处理。Activity
生效,意味着可以针对单个页面进行刘海屏适配,设置了该属性的Activity
系统将不会做特殊处理。华为的适配使用了反射相关操作代码如下:
//判断是否是华为手机
public final boolean isHuaWei() {
String manufacturer = Build.MANUFACTURER;
if (!TextUtils.isEmpty(manufacturer)){
if (manufacturer.contains("HUAWEI")) return true;
}
return false;
}
//判断是否存有刘海屏
@RequiresApi(api = Build.VERSION_CODES.O)
@Override
public boolean hasNotBangScreen(Window window) {
if (isHaveResult) return isBangScreen;
try {
ClassLoader huaWeiClassLoader = window.getContext().getClassLoader();
Class HwNotchSizeUtil = huaWeiClassLoader.loadClass("com.huawei.android.util.HwNotchSizeUtil");
Method method = HwNotchSizeUtil.getMethod("hasNotchInScreen");
isHaveResult = true;
return isBangScreen = (boolean) method.invoke(HwNotchSizeUtil);
} catch (ClassNotFoundException e) {
Log.e(TAG, "hasNotchInScreen ClassNotFoundException");
} catch (NoSuchMethodException e) {
Log.e(TAG, "hasNotchInScreen NoSuchMethodException");
} catch (Exception e) {
Log.e(TAG, "hasNotchInScreen Exception");
} finally {
isHaveResult = true;
return isBangScreen;
}
}
//获取刘海屏尺寸相关信息
@RequiresApi(api = Build.VERSION_CODES.O)
@Override
public List getBangSize(Window window) {
ArrayList result = new ArrayList();
if (window != null) {
Rect rect = new Rect();
try {
Context context = window.getContext();
if (hwBangSizeUtil == null && context != null) {
ClassLoader cl = context.getClassLoader();
hwBangSizeUtil = cl.loadClass("com.huawei.android.util.HwNotchSizeUtil");
}
if (hwBangSizeUtil == null) {
return result;
}
Method get = hwBangSizeUtil.getMethod("getNotchSize");
int[] ret = (int[]) get.invoke(hwBangSizeUtil);
if (ret == null) {
return result;
} else {
Resources resources = context.getResources();
if (resources != null) {
rect.left = (resources.getDisplayMetrics().widthPixels - ret[0]) / 2;
rect.bottom = ret[1];
rect.right = rect.left + ret[0];
rect.top = 0;
result.add(rect);
}
return result;
}
} catch (Exception e) {
Log.e(TAG, e.getMessage());
return result;
}
} else return result;
}
vivo在设置--显示与亮度--第三方应用显示比例中可以切换是否全屏显示还是安全区域显示
相关代码如下:
//判断是不是Vivo手机
public final boolean isVivo() {
String manufacturer = this.getSystemProperty("ro.vivo.os.name");
if (!TextUtils.isEmpty(manufacturer)){
return true;
}
return false;
}
//获取手机是否是刘海屏手机
@RequiresApi(api = Build.VERSION_CODES.O)
@Override
public boolean hasNotBangScreen(Window window) {
if (window == null)
return false;
if (vivo == null) {
ClassLoader vivoLoader = window.getContext().getClassLoader();
try {
vivo = vivoLoader.loadClass("android.util.FtFeature");
vivoMethod = vivo.getMethod("isFeatureSupport", Integer.TYPE);
return (boolean) vivoMethod.invoke(vivo, 0x00000020);
} catch (ClassNotFoundException e) {
logError(e);
return false;
} catch (NoSuchMethodException e) {
logError(e);
return false;
} catch (IllegalAccessException e) {
logError(e);
return false;
} catch (InvocationTargetException e) {
logError(e);
return false;
}
} else {
if (vivoMethod == null) {
try {
vivoMethod = vivo.getMethod("isFeatureSupport", Integer.TYPE);
} catch (NoSuchMethodException e) {
logError(e);
return false;
}
try {
return (boolean) vivoMethod.invoke(vivo, 0x00000020);
} catch (IllegalAccessException e) {
logError(e);
return false;
} catch (InvocationTargetException e) {
logError(e);
return false;
}
}
}
return false;
}
//获取刘海屏尺寸信息
@RequiresApi(api = Build.VERSION_CODES.O)
@Override
public List getBangSize(Window window) {
List result = new ArrayList<>();
if (window == null) return result;
Rect rect = new Rect();
DisplayMetrics displayMetrics = window.getContext().getResources().getDisplayMetrics();
int notchWidth = (int) TypedValue.applyDimension(1, 100.0F, displayMetrics);
int notchHeight = (int) TypedValue.applyDimension(1, 27.0F, displayMetrics);
rect.left = (displayMetrics.widthPixels - notchWidth) / 2;
rect.right = rect.left + notchWidth;
rect.top = 0;
rect.bottom = notchHeight;
result.add(rect);
return result;
}
//判断是否是小米
public final boolean isMiui() {
String manufacturer = getSystemProperty("ro.miui.ui.version.name");
if (!TextUtils.isEmpty(manufacturer)){
return true;
}
return false;
}
//判断是否是刘海屏
@RequiresApi(api = Build.VERSION_CODES.O)
@Override
public boolean hasNotBangScreen(Window window) {
return "1".equals(SystemProperties.getSingle().get("ro.miui.notch"));
}
//由于小米的状态栏高度略大于或等于刘海屏高度故这样封装
@RequiresApi(api = Build.VERSION_CODES.O)
@Override
public List getBangSize(Window window) {
List result = new ArrayList<>();
if (window == null) return result;
Context context = window.getContext();
Resources resources = context.getResources();
Rect rect = new Rect();
if (resources != null) {
rect.left = 0;
rect.bottom = BangScreenTools.getBangScreenTools().getStatusBarHeight(context);
rect.right = resources.getDisplayMetrics().widthPixels;
rect.top = 0;
result.add(rect);
}
return result;
}
//判断是否是oppo
public final boolean isOppo() {
String manufacturer = getSystemProperty("ro.product.brand");
if (!TextUtils.isEmpty(manufacturer)){
return true;
}
return false;
}
//判断是否是刘海屏
@RequiresApi(api = Build.VERSION_CODES.O)
@Override
public boolean hasNotBangScreen(Window window) {
if (window == null) return false;
return window.getContext().getPackageManager().hasSystemFeature("com.oppo.feature.screen.heteromorphism");
}
//目前Oppo刘海屏机型尺寸规格都是统一的,显示屏宽度为1080px,高度为2280px,刘海区域宽度为324px, 高度为80px
@RequiresApi(api = Build.VERSION_CODES.O)
@Override
public List getBangSize(Window window) {
List result = new ArrayList<>();
if (window == null) return result;
DisplayMetrics displayMetrics = window.getContext().getResources().getDisplayMetrics();
Rect rect = new Rect();
int width = 324;
int height = 80;
rect.left = (displayMetrics.widthPixels - width) / 2;
rect.right = rect.left + width;
rect.bottom = height;
rect.top = 0;
result.add(rect);
return result;
}
make the statusbar was transparent
//used onCreat method
BangScreenTools.getBangScreenTools().transparentStatusCutout(window, this)
//used onWindowFocusChanged method
BangScreenTools.getBangScreenTools().windowChangeTransparentStatusCutout(window)
make the layout extend statusbar
//used onCreat method
BangScreenTools.getBangScreenTools().extendStatusCutout(window, this)
//used onWindowFocusChanged method
BangScreenTools.getBangScreenTools().windowChangeExtendStatusCutout(window)
make the layout was fullscreen
//used onCreat method
BangScreenTools.getBangScreenTools().fullscreen(window, this)
//used onWindowFocusChanged method,this void is make fullscreen is worked.
BangScreenTools.getBangScreenTools().windowChangeFullscreen(window)
make the layout not use bangscreen
//used onCreat method
BangScreenTools.getBangScreenTools().blockDisplayCutout(window)
这是AndroidP相关Api的介绍接下来展示下封装后代码运行的效果