判断全面屏是否有虚拟导航栏的方法isNavigationBarExist是无效的,搜索了很多方法都不生效,原因目前未知,猜测可能是定制ROM并没有按照标准的系统设置,而是各厂家使用了自己的处理
安卓设备在一些屏幕适应计算中通常需要获取屏幕的高度。在非全面屏的设备中,获取屏幕高度可以使用下面的方法,可以获取到包含屏幕顶部通知栏但不包含底部虚拟导航栏的屏幕高度:
- 方法一
DisplayMetrics dm = context.getResources().getDisplayMetrics();
int w = dm.widthPixels;
int h = dm.heightPixels;
if (w < h)
return h;
else
return w;
但是,在全面屏设备中这个方法是不能正常工作的,获取到的屏幕高度比实际屏幕高度要小,填充后底部会出现一个黑色的矩形区域。解决的方法是使用下面的方法获取一个全屏幕的高度,包含顶部通知栏又包含底部虚拟导航栏的屏幕高度:
- 获取包含顶部通知栏和底部虚拟导航栏的屏幕高度
WindowManager windowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics metrics = new DisplayMetrics();
windowManager.getDefaultDisplay().getRealMetrics(metrics);
int w = metrics.widthPixels;
int h = metrics.heightPixels;
if (w < h)
return h;
else
return w;
这个计算结果和方法一相比,多了底部虚拟导航栏的高度,这就需要减去它,使得两种结果所获取到的高度包含的区域相同。
- 获取屏底部虚拟导航栏高度:获取导航栏高度是通过获取资源定义的高度获取的,但是不能直接获取这个资源设置还需要判断是否真的有导航栏,因为有些手机厂商例如小米,虽然没有导航栏但是却保留了这个资源的定义。
- 判断是否有导航栏:依据-是否-有返回键判断是否有底部虚拟导航栏(假定全面屏手机不会有物理按键),但是在华为和荣耀手机上这个方法并不凑效,使用了单独的方法进行判断。
int getVirtualNavigationBarHeight(Context context) {
int result = 0;
if (isNavigationBarExist(context)) {
int resourceId =context.getResources().getIdentifier("navigation_bar_height","dimen", "android");
if (resourceId > 0) {
result = context.getResources().getDimensionPixelSize(resourceId);
}
}
return result;
}
boolean isNavigationBarExist(@NonNull Context context) {
if ("HUAWEI".equals(Build.BRAND) || "HONOR".equals(Build.BRAND)){
return isNavigationBarShow_HUIWEI(context);
}
return KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_BACK);
}
boolean isNavigationBarShow_HUIWEI(Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1){
WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display display = windowManager.getDefaultDisplay();
Point size = new Point();
Point realSize = new Point();
display.getSize(size);
display.getRealSize(realSize);
return realSize.y != size.y;
} else {
boolean menu = ViewConfiguration.get(context).hasPermanentMenuKey();
boolean back = KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_BACK);
if (menu || back) {
return false;
} else {
return true;
}
}
}
- 方法二
int getRawDeviceScreenHeight(@NonNull Context context) {
WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics metrics = new DisplayMetrics();
windowManager.getDefaultDisplay().getRealMetrics(metrics);
int navBarHeight = getVirtualNavigationBarHeight(context);
int w = metrics.widthPixels;
int h = metrics.heightPixels;
if (w < h)
return h - navBarHeight;
else
return w - navBarHeight;
}
两个方法都有了,但是我们要对使用场景做一个选择,还需要判断手机是否是全面屏,判断全面屏根据是屏幕的高/宽值,这里设定阈值为1.97:
boolean isAllScreenDevice(Context context) {
// 低于 API 21的,都不会是全面屏。。。
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
return false;
}
WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
if (windowManager != null) {
Display display = windowManager.getDefaultDisplay();
Point point = new Point();
display.getRealSize(point);
float width, height;
if (point.x < point.y) {
width = point.x;
height = point.y;
} else {
width = point.y;
height = point.x;
}
if (height / width >= 1.97f) {
return true;
}
}
return false;
}
综上,获取安卓屏幕高度的方法为:
int getDeviceScreenHeight(@NonNull Context context) {
if (context == null) {
return 1;
}
if (isAllScreenDevice(context)) {
return getRawDeviceScreenHeight(context);
}
DisplayMetrics dm = context.getResources().getDisplayMetrics();
int w = dm.widthPixels;
int h = dm.heightPixels;
if (w < h)
return h;
else
return w;
}
如果还需要减去顶部通知栏的高度的话,这里给出获取顶部通知栏高度的方法:
- 获取顶部通知栏高度
int getStatusBarHeight(@NonNull Context context) {
int result = 0;
int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
result = context.getResources().getDimensionPixelSize(resourceId);
}
return result;
}