本篇文章主要总结一下在全面屏上获取高度的问题。
一般 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 获取到的方法出现一些偏差,最明显的就是在一些设备上如果用户隐藏了导航键使用全面屏手势,这个方法返回的要比实际的小。具体可以看下表
设备导航键类型系统版本高度getScreenHeight1加3T物理8.0.019201920Nexus 6虚拟7.1.125602392诺基亚x6隐藏8.1.022802154诺基亚x6虚拟8.1.022802154荣耀7X虚拟8.0.021602038荣耀7X隐藏8.0.021602160诺基亚x71隐藏9.023102081诺基亚x71虚拟9.023102081小米mix3隐藏9.023402210oppoR15隐藏9.022802280oppoR15虚拟9.022802056可以大概看出来在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 = 2content://settings/secure/hide_navigationbar_enable 就是我们要监听的内容,对应的就是 Settings.Secure.getInt(context.getContentResolver(), "hide_navigationbar_enable", 0) ,以次类推。