本篇文章主要总结一下在全面屏上获取高度的问题。
一般 Android 上获取设备的高度都是通过 DefaultDisplay 的方式来获取的如下:
public int getScreenHeight(Activity activity){
WindowManager manage = activity.getWindowManager();
Display display = manage.getDefaultDisplay();
return display.getHeight();
}
不过后来 Display.getHeight 被标记过时了,所以就用下面这个方式来代替:
public int getScreenHeight(Activity activity){
WindowManager manage = activity.getWindowManager();
DisplayMetrics dm = new DisplayMetrics();
return manage.getDefaultDisplay().getMetrics(dm).heightPixels;
}
上面两种方法在之前的设备上都是没有问题的,但是随着 Android 设备的发展,虚拟导航键、全面屏手势的普及加之不同的厂商对这个方法的处理不相同,导致很多时候通过 getScreenHeight 获取到的方法出现一些偏差,最明显的就是在一些设备上如果用户隐藏了导航键使用全面屏手势,这个方法返回的要比实际的小。具体可以看下表
设备 | 导航键类型 | 系统版本 | 高度 | getScreenHeight |
---|---|---|---|---|
1加3T | 物理 | 8.0.0 | 1920 | 1920 |
Nexus 6 | 虚拟 | 7.1.1 | 2560 | 2392 |
诺基亚x6 | 隐藏 | 8.1.0 | 2280 | 2154 |
诺基亚x6 | 虚拟 | 8.1.0 | 2280 | 2154 |
荣耀7X | 虚拟 | 8.0.0 | 2160 | 2038 |
荣耀7X | 隐藏 | 8.0.0 | 2160 | 2160 |
诺基亚x71 | 隐藏 | 9.0 | 2310 | 2081 |
诺基亚x71 | 虚拟 | 9.0 | 2310 | 2081 |
小米mix3 | 隐藏 | 9.0 | 2340 | 2210 |
oppoR15 | 隐藏 | 9.0 | 2280 | 2280 |
oppoR15 | 虚拟 | 9.0 | 2280 | 2056 |
可以大概看出来在9.0以下的手机上 getScreenHeight 如果有虚拟导航键获取到的是真实的设备屏幕高度,即除去虚拟导航键的高度,如果没有虚拟导航键则各个手机不一样;在9.0的手机上也是表现各不相同,所以需要有一个根据不同的情况来判断。
获取真实的设备高度可以使用 Deisplay.getRealSize 来获取,但是这个获取到的是设备高度,所以在有虚拟导航键的情况可以使用 getScreenHeight 来获取高度,在隐藏虚拟导航键的情况下可以使用 Deisplay.getRealSize 来获取:
/**
* 判断设备的真实高度,即app界面真实使用的高度
*
* @return
*/
public static int getRealScreenHeight(Context context) {
if (!isNavBarHide(context)) {
// 如果没有隐藏导航键则正常返回
return DeviceInfo.getScreenHeight();
}
try {
Point point = new Point();
((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getRealSize(point);
return point.y;
} catch (Exception e) {
e.printStackTrace();
return DeviceInfo.getScreenHeight();
}
}
下面看一下怎么判断是否隐藏了虚拟导航键。
判断是否隐藏了导航键,可以通过监听一个 ContentResolver 来实现,其实下面的方法经过验证正确可用:
/**
* 是否隐藏了导航键
*
* @param context
* @return
*/
public static boolean isNavBarHide(Context context) {
try {
String brand = Build.BRAND;
// 这里做判断主要是不同的厂商注册的表不一样
if (!Utils.isStringEmpty(brand) && (brand.equalsIgnoreCase("VIVO") || brand.equalsIgnoreCase("OPPO"))) {
return Settings.Secure.getInt(context.getContentResolver(), getDeviceForceName(), 0) != 0;
} else if (!Utils.isStringEmpty(brand) && brand.equalsIgnoreCase("Nokia")) {
//甚至 nokia 不同版本注册的表不一样, key 还不一样。。。
return Settings.Secure.getInt(context.getContentResolver(), "swipe_up_to_switch_apps_enabled", 0) == 1
|| Settings.System.getInt(context.getContentResolver(), "navigation_bar_can_hiden", 0) != 0;
} else
return Settings.Global.getInt(context.getContentResolver(), getDeviceForceName(), 0) != 0;
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
/**
* 各个手机厂商注册导航键相关的 key
*
* @return
*/
public static String getDeviceForceName() {
String brand = Build.BRAND;
if (Utils.isStringEmpty(brand)) return "navigationbar_is_min";
if (brand.equalsIgnoreCase("HUAWEI") || "HONOR".equals(brand)) {
return "navigationbar_is_min";
} else if (brand.equalsIgnoreCase("XIAOMI")) {
return "force_fsg_nav_bar";
} else if (brand.equalsIgnoreCase("VIVO")) {
return "navigation_gesture_on";
} else if (brand.equalsIgnoreCase("OPPO")) {
return "hide_navigationbar_enable";
} else if (brand.equalsIgnoreCase("samsung")) {
return "navigationbar_hide_bar_enabled";
} else if (brand.equalsIgnoreCase("Nokia")) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
return "navigation_bar_can_hiden";
} else {
return "swipe_up_to_switch_apps_enabled";
}
} else {
return "navigationbar_is_min";
}
}
也有很多资料里面给出了相似的方法,但是有些给出的key以及表是不对的,比如 oppo 、诺基亚的,关于这些key 除了参考厂商给出的资料外,也可以通过观察系统日志的方法来获取,比如 oppo 在切换导航方式的时候会有下面的 log:
2019-10-24 17:39:23.660 1376-1376/? V/SettingsProvider: Notifying for 0: content://settings/secure/hide_navigationbar_enable
2019-10-24 17:39:23.661 20487-20487/? I/StatusBar: mHideNavigationBarObserver mode:2
2019-10-24 17:39:23.661 1376-1402/? V/SettingsProvider: getSecureSetting(wake_gesture_enabled, getCallingPackage = android
2019-10-24 17:39:23.662 5100-5100/? I/ColorNavigationBarUtil: setImePackageInGestureMode isImeInGestureMode:true
2019-10-24 17:39:23.662 1376-1402/? D/WindowManager: updateSettings: incallPowerButtonHangup = 0
2019-10-24 17:39:23.663 1376-1402/? D/WindowManager: updateSettings: powerButtonEndsAlarmclock = 0
2019-10-24 17:39:23.664 20577-20577/? I/ColorRecentsStateController: onChange() mNavbarEnable = 2
content://settings/secure/hide_navigationbar_enable
就是我们要监听的内容,对应的就是 Settings.Secure.getInt(context.getContentResolver(), "hide_navigationbar_enable", 0)
,以次类推。