Android 获取屏幕高度,虚拟导航键检测

本篇文章主要总结一下在全面屏上获取高度的问题。

获取屏幕高度

一般 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.019201920
Nexus 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.023402210
oppoR15隐藏9.022802280
oppoR15虚拟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 = 2

content://settings/secure/hide_navigationbar_enable 就是我们要监听的内容,对应的就是 Settings.Secure.getInt(context.getContentResolver(), "hide_navigationbar_enable", 0) ,以次类推。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值