56

我正在尝试检查加载时是否存在android导航栏,以便我可以相应地调整布局,有人有什么建议吗?

这是我要检测的导航栏: 在此处输入图像描述

PS 到目前为止,我发现的所有方法都是尝试删除栏的“坏”方法,我不想这样做。

4

12 回答 12

50

花了我一些时间,但我找到了一种比依赖hasPermanentMenuKey()更可靠的方法,它不适用于像HTC One这样没有菜单键但确实有主页和返回键的新手机,所以不需要(或显示)软导航栏。为了解决这个问题,请尝试以下代码来检查后退按钮:

boolean hasMenuKey = ViewConfiguration.get(context).hasPermanentMenuKey();
boolean hasBackKey = KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_BACK);

if(!hasMenuKey && !hasBackKey) {
    // Do whatever you need to do, this device has a navigation bar
}
于 2013-05-17T12:00:17.573 回答
35

没有可靠的方法来检查导航栏。使用KeyCharacterMap.deviceHasKey您可以检查设备上是否存在某些物理键,但此信息不是很有用,因为具有物理键的设备仍然可以具有导航栏。像 OnePlus One 这样的设备,或任何运行自定义 rom 的设备,在设置中都有一个禁用物理键的选项,并添加了一个导航栏。无法检查此选项是否已启用,并且deviceHasKey对于被此选项禁用的键仍然返回 true。

这是你能得到的最接近的:

boolean hasBackKey = KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_BACK);
boolean hasHomeKey = KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_HOME);

if (hasBackKey && hasHomeKey) {
    // no navigation bar, unless it is enabled in the settings
} else {
    // 99% sure there's a navigation bar
}

如果返回和主页按钮在设备上没有物理存在,则它必须有一个导航栏,否则用户将根本无法导航。但是,您永远无法 100% 确定这一点,因为制造商可能实施deviceHasKey错误。

于 2014-04-20T10:49:00.073 回答
12

另一个解决方案(我的类UtilsUISystem的一部分)

    public static boolean hasNavBar (Resources resources)
    {
        //Emulator
        if (Build.FINGERPRINT.startsWith("generic"))
            return true;

        int id = resources.getIdentifier("config_showNavigationBar", "bool", "android");
        return id > 0 && resources.getBoolean(id);
    }
于 2015-01-30T11:38:11.163 回答
9

这是一个结合 Pauland 和 Philask 解决方案的快速答案。不过,恐怕我没有足够的设备来测试它是否可以在任何地方使用。我很想听听其他人的结果。

boolean hasNavBar(Context context) {
    Resources resources = context.getResources();
    int id = resources.getIdentifier("config_showNavigationBar", "bool", "android");
    if (id > 0) {
        return resources.getBoolean(id);
    } else {    // Check for keys
        boolean hasMenuKey = ViewConfiguration.get(context).hasPermanentMenuKey();
        boolean hasBackKey = KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_BACK);
        return !hasMenuKey && !hasBackKey;
    }
}
于 2015-03-18T11:01:28.733 回答
6

我已经这样做了,它适用于我测试的每台设备,甚至在模拟器上:

public static boolean hasNavigationBar(Activity activity) {
    Rect rectangle = new Rect();
    DisplayMetrics displayMetrics = new DisplayMetrics();
    activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(rectangle);
    activity.getWindowManager().getDefaultDisplay().getRealMetrics(displayMetrics);
    return displayMetrics.heightPixels != (rectangle.top + rectangle.height());
}
于 2020-06-10T10:47:33.863 回答
5

您可以将此代码添加到活动的 onCreate() 方法中:

 View decorView = getWindow().getDecorView();
decorView.setOnSystemUiVisibilityChangeListener
        (new View.OnSystemUiVisibilityChangeListener() {
            @Override
            public void onSystemUiVisibilityChange(int visibility) {
                if ((visibility & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0) {
                    // TODO: The navigation bar is visible. Make any desired
                    // adjustments to your UI, such as showing the action bar or
                    // other navigational controls.
                } else {
                    // TODO: The navigation bar is NOT visible. Make any desired
                    // adjustments to your UI, such as hiding the action bar or
                    // other navigational controls.
                }
            }
        });
于 2016-08-11T08:43:45.400 回答
3

这种方法对我有用

    int id = getResources().getIdentifier("config_showNavigationBar","bool","android");
        boolean result = id > 0 && getResources().getBoolean(id);
//
        if(result) {

            // Do whatever you need to do, this device has a soft Navigation Bar
        }

它对我有用,并在许多设备上进行了测试。

于 2017-05-24T20:49:15.347 回答
2
boolean hasNavBar(Context context) {
    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
        // navigation bar was introduced in Android 4.0 (API level 14)
        Resources resources = context.getResources();
        int id = resources.getIdentifier("config_showNavigationBar", "bool", "android");
        if (id > 0) {
            return resources.getBoolean(id);
        } else {    // Check for keys
            boolean hasMenuKey = ViewConfiguration.get(context).hasPermanentMenuKey();
            boolean hasBackKey = KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_BACK);
            return !hasMenuKey && !hasBackKey;
        }
    } else {
        return false;
    }
}
于 2016-02-16T07:50:02.703 回答
1

可能更好的方法是测量屏幕。

从 API 17 开始,有getWindowManager().getDefaultDisplay().getRealSize(),可以与返回的大小进行比较getWindowManager().getDefaultDisplay().getSize()

如果你得到不同的结果,我认为可以肯定地说有一个导航栏,如果你得到相同的结果,就没有导航栏。需要注意的一件事是您的目标 SDK 和支持的屏幕,getSize()如果 Android 认为您的应用在不缩放的情况下无法在当前设备上正常运行,这可能会导致结果被缩放。

getWindowManager().getDefaultDisplay().getMetrics()在 API 17 以下,您可以通过横向和纵向模式测量屏幕,同样,不同的结果可能意味着有一个导航栏。

但是,如果您获得相同的结果,您实际上并不知道,因为即使在横向时,手机也可以将导航栏保持在较短的边缘。有根据的猜测是,如果宽度或高度比标准尺寸(如1280x800, 1280x720, )小 4% 到 8% 1024x600,而另一个尺寸相等,那么可能还有一个导航栏。不过,不要赌它。有太多的分辨率,它们之间的差异太小,无法正常工作。

于 2015-05-21T07:33:43.383 回答
0

我看到上面的答案,我想表明“不存在”可以视为高度为0;所以它可以是这样的:

public static int getScreenH(Context context) {
    DisplayMetrics dm = new DisplayMetrics();
    dm = context.getResources().getDisplayMetrics();
    int h = dm.heightPixels;
    return h;
}

public static int getDpi(Context context) {

    DisplayMetrics displayMetrics1 = context.getResources().getDisplayMetrics();
    int height1 = displayMetrics1.heightPixels;

    int dpi = 0;
    WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
    Display display = windowManager.getDefaultDisplay();
    DisplayMetrics displayMetrics = new DisplayMetrics();

    @SuppressWarnings("rawtypes")
    Class c;
    try {
        c = Class.forName("android.view.Display");
        @SuppressWarnings("unchecked")
        Method method = c.getMethod("getRealMetrics", DisplayMetrics.class);
        method.invoke(display, displayMetrics);
        dpi = displayMetrics.heightPixels;
    } catch (Exception e) {
        e.printStackTrace();
    }
    return dpi;
}

public static int getBottomStatusHeight(Context context) {
    int totalHeight = getDpi(context);

    int contentHeight = getScreenH(context);

    return totalHeight - contentHeight;
}

```

于 2016-11-15T03:42:04.393 回答
0

在 Android 10(API 级别 29)上,您还可以检查底部窗口插图:

@RequiresApi(api = Build.VERSION_CODES.Q)
private boolean hasNavigationBar() {
    final WindowInsets windowInsets = getWindow().getDecorView().getRootWindowInsets();
    if (windowInsets == null) {
            throw new RuntimeException("Window is not attached");
    }
    return windowInsets.getTappableElementInsets().bottom > 0;
}

请注意,必须附加窗口getRootWindowInsets()才能返回非空值,因此您可能希望在onAttachedToWindow.

LineageOS 的启动器应用程序 Trebuchet (来源)也使用了这个解决方案,这就是我了解它的方式。

于 2021-09-26T07:30:23.397 回答
-2

解决方案:只有没有永久硬件密钥的设备才有导航栏,因此您可以检查 API 版本并使用 hasPermanentMenuKey() 查找硬件密钥

 boolean hasMenuKey = ViewConfiguration.get(context).hasPermanentMenuKey();
于 2013-04-19T15:17:22.743 回答