如何以编程方式获取Android导航栏的高度和宽度?

Kev*_*vik 113 graphics user-interface android android-activity

Android屏幕底部的黑色导航栏无法轻松移除.它从3.0开始就是Android的一部分,作为硬件按钮的替代品.这是一张图片:

系统栏

如何获得此UI元素的宽度和高度的大小(以像素为单位)?

San*_*ket 168

试试以下代码:

Resources resources = context.getResources();
int resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android");
if (resourceId > 0) {
    return resources.getDimensionPixelSize(resourceId);
}
return 0;
Run Code Online (Sandbox Code Playgroud)

  • 请注意,这段代码在没有导航栏的设备上不返回0.在三星S2和S3上测试过.我得到72和96. (51认同)
  • 谢谢.+1.你知道这种方法有多稳定吗?资源标识符是否会在不同平台版本之间发生变化? (5认同)
  • 此代码显示导航栏的**默认**大小(横向模式下的导航栏高度也可以使用`navigation_bar_height_landscape`,垂直导航栏的宽度可以使用`navigation_bar_width`).您必须单独找出导航栏实际显示的位置和位置,例如通过测试是否存在物理菜单按钮.也许您可以在Android源代码中找到其他方法,网址为https://android.googlesource.com/platform/frameworks/base/+/android-4.2.2_r1/tools/layoutlib/bridge/src/com/android/layoutlib /bridge/impl/RenderSessionImpl.java (5认同)
  • @Egidijus看看我的答案,对于有物理导航的设备,它将返回0 http://stackoverflow.com/a/29938139/1683141 (4认同)

Egi*_*gis 99

我通过将应用程序可用的屏幕大小与实际屏幕大小进行比较来获得导航栏大小 我认为当应用程序可用的屏幕尺寸小于实际屏幕尺寸时,存在导航栏.然后我计算导航栏的大小.此方法适用于API 14及更高版本.

public static Point getNavigationBarSize(Context context) {
    Point appUsableSize = getAppUsableScreenSize(context);
    Point realScreenSize = getRealScreenSize(context);

    // navigation bar on the side
    if (appUsableSize.x < realScreenSize.x) {
        return new Point(realScreenSize.x - appUsableSize.x, appUsableSize.y);
    }

    // navigation bar at the bottom
    if (appUsableSize.y < realScreenSize.y) {
        return new Point(appUsableSize.x, realScreenSize.y - appUsableSize.y);
    }

    // navigation bar is not present
    return new Point();
}

public static Point getAppUsableScreenSize(Context context) {
    WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
    Display display = windowManager.getDefaultDisplay();
    Point size = new Point();
    display.getSize(size);
    return size;
}

public static Point getRealScreenSize(Context context) {
    WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
    Display display = windowManager.getDefaultDisplay();
    Point size = new Point();

    if (Build.VERSION.SDK_INT >= 17) {
        display.getRealSize(size);
    } else if (Build.VERSION.SDK_INT >= 14) {
        try {
            size.x = (Integer) Display.class.getMethod("getRawWidth").invoke(display);
            size.y = (Integer) Display.class.getMethod("getRawHeight").invoke(display);
        } catch (IllegalAccessException e) {} catch (InvocationTargetException e) {} catch (NoSuchMethodException e) {}
    }

    return size;
}
Run Code Online (Sandbox Code Playgroud)

  • 这是我所测试的所有不同设备中唯一适用于我的解决方案.我需要确定导航栏是否位于屏幕底部,并考虑了方向.我稍微调整了答案中的代码,只返回屏幕底部导航栏的大小,0表示它不存在.我的测试表明,Nexus5 =肖像:144,风景:0 | Nexus7 =肖像:96,风景:96 | 三星Note II =肖像:0,风景:0 | 三星S10 =肖像:0,风景:0 (2认同)
  • 我还可以确认 Danylo 的解决方案仅适用于某些型号。Egidijus 的这个解决方案是一个更好的工作解决方案,并且适用于迄今为止测试的所有模型。这次真是万分感谢。 (2认同)
  • 状态栏是应用程序可用屏幕的一部分. (2认同)
  • 这也没有考虑到 DisplayCutout (2认同)

Mdl*_*dlc 35

NavigationBar高度因某些设备而异,但也适用于某些设备.首先,您必须检查设备是否有导航栏,然后是设备是平板电脑还是非平板电脑(手机),最后您必须查看设备的方向才能获得正确的高度.

public int getNavBarHeight(Context c) {
         int result = 0;
         boolean hasMenuKey = ViewConfiguration.get(c).hasPermanentMenuKey();
         boolean hasBackKey = KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_BACK);

         if(!hasMenuKey && !hasBackKey) {
             //The device has a navigation bar
             Resources resources = c.getResources();

             int orientation = resources.getConfiguration().orientation;
             int resourceId;
             if (isTablet(c)){
                 resourceId = resources.getIdentifier(orientation == Configuration.ORIENTATION_PORTRAIT ? "navigation_bar_height" : "navigation_bar_height_landscape", "dimen", "android");
             }  else {
                 resourceId = resources.getIdentifier(orientation == Configuration.ORIENTATION_PORTRAIT ? "navigation_bar_height" : "navigation_bar_width", "dimen", "android");     
             }

             if (resourceId > 0) {
                 return resources.getDimensionPixelSize(resourceId);
             }
         }
         return result;
} 


private boolean isTablet(Context c) {
    return (c.getResources().getConfiguration().screenLayout
            & Configuration.SCREENLAYOUT_SIZE_MASK)
            >= Configuration.SCREENLAYOUT_SIZE_LARGE;
}
Run Code Online (Sandbox Code Playgroud)

  • 我在Nexus 5上测试过这段代码,两者都返回false.你确定你不是在模拟器或ROM吗? (5认同)
  • 不幸的是,虽然应该正确,但不能在Sony Xperia Z3上运行“ hasBackKey = false!”。代替了下面的方法:boolean navBarExists = getResources()。getBoolean(getResources()。getIdentifier(“ config_showNavigationBar”,“ bool”,“ android”))); (2认同)

Dan*_*okh 26

实际上平板电脑上的导航栏(至少是Nexus 7)在纵向和横向上有不同的大小,所以这个功能应该如下所示:

private int getNavigationBarHeight(Context context, int orientation) {
    Resources resources = context.getResources();

    int id = resources.getIdentifier(
            orientation == Configuration.ORIENTATION_PORTRAIT ? "navigation_bar_height" : "navigation_bar_height_landscape",
            "dimen", "android");
    if (id > 0) {
        return resources.getDimensionPixelSize(id);
    }
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

  • 要从上下文中检索方向,请使用`context.getResources().getConfiguration().orientation` (6认同)

Joh*_*ohn 15

我认为更正确的答案在这里,因为它也允许你采取甚至切割高度.

获取您的根视图,并向其添加setOnApplyWindowInsetsListener(或者您可以从中覆盖onApplyWindowInsets),并从中获取insets.getSystemWindowInsets.

在我的相机活动中,我添加等于systemWindowInsetBottom的填充到我的底部布局.最后,它解决了切断问题.

相机活动插入

与appcompat它是这样的

ViewCompat.setOnApplyWindowInsetsListener(mCameraSourcePreview, (v, insets) -> {
    takePictureLayout.setPadding(0,0,0,insets.getSystemWindowInsetBottom());
    return insets.consumeSystemWindowInsets();
});
Run Code Online (Sandbox Code Playgroud)

没有appcompat,这个:

mCameraSourcePreview.setOnApplyWindowInsetsListener((v, insets) -> { ... })
Run Code Online (Sandbox Code Playgroud)

  • 当我从 onCreate() 调用它时,侦听器永远不会执行。我错过了什么吗? (4认同)

小智 10

我希望这可以帮助你

public int getStatusBarHeight() {
    int result = 0;
    int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
    if (resourceId > 0) {
        result = getResources().getDimensionPixelSize(resourceId);
    }
    return result;
}

public int getNavigationBarHeight()
{
    boolean hasMenuKey = ViewConfiguration.get(context).hasPermanentMenuKey();
    int resourceId = getResources().getIdentifier("navigation_bar_height", "dimen", "android");
    if (resourceId > 0 && !hasMenuKey)
    {
        return getResources().getDimensionPixelSize(resourceId);
    }
    return 0;
}
Run Code Online (Sandbox Code Playgroud)


Hay*_*mar 7

2021 年新答案来拯救


灵感来自Egis的回答:

context.navigationBarHeight
Run Code Online (Sandbox Code Playgroud)

扩展吸气剂在哪里

val Context.navigationBarHeight: Int
get() {
    val windowManager = getSystemService(Context.WINDOW_SERVICE) as WindowManager

    return if (Build.VERSION.SDK_INT >= 30) {
        windowManager
                .currentWindowMetrics
                .windowInsets
                .getInsets(WindowInsets.Type.navigationBars())
                .bottom

    } else {
        val currentDisplay = try {
            display
        } catch (e: NoSuchMethodError) {
            windowManager.defaultDisplay
        }

        val appUsableSize = Point()
        val realScreenSize = Point()
        currentDisplay?.apply {
            getSize(appUsableSize)
            getRealSize(realScreenSize)
        }

        // navigation bar on the side
        if (appUsableSize.x < realScreenSize.x) {
            return realScreenSize.x - appUsableSize.x
        }

        // navigation bar at the bottom
        return if (appUsableSize.y < realScreenSize.y) {
            realScreenSize.y - appUsableSize.y
        } else 0
    }
}
Run Code Online (Sandbox Code Playgroud)

测试于:

  • 带导航栏的模拟器
    • 像素 3a(API 30)
    • 像素 2(API 28)
    • 像素 3(API 25)
    • 像素 2(API 21)
  • 小米 Poco f2 pro 带导航栏和不带导航栏(全屏)

  • 它总是返回 navigation + status 。 (3认同)

小智 5

这是我将paddingRight和paddingBottom添加到View以避开导航栏的代码.我在这里结合了一些答案,并与isInMultiWindowMode一起制作了横向定向的特殊条款.关键是要阅读navigation_bar_height,还要检查config_showNavigationBar以确保我们应该实际使用高度.

以前的解决方案都不适合我.从Android 7.0开始,您必须考虑多窗口模式.这打破了将display.realSizedisplay.size进行比较的实现,因为realSize为您提供了整个屏幕的尺寸(两个分割窗口),而尺寸只为您提供了应用窗口的尺寸.将填充设置为此差异将使整个视图成为填充.

/** Adds padding to a view to dodge the navigation bar.

    Unfortunately something like this needs to be done since there
    are no attr or dimens value available to get the navigation bar
    height (as of December 2016). */
public static void addNavigationBarPadding(Activity context, View v) {
    Resources resources = context.getResources();
    if (hasNavigationBar(resources)) {
        int orientation = resources.getConfiguration().orientation;
        int size = getNavigationBarSize(resources);
        switch (orientation) {
        case Configuration.ORIENTATION_LANDSCAPE:
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N &&
                context.isInMultiWindowMode()) { break; }
            v.setPadding(v.getPaddingLeft(), v.getPaddingTop(),
                         v.getPaddingRight() + size, v.getPaddingBottom());
            break;
        case Configuration.ORIENTATION_PORTRAIT:
            v.setPadding(v.getPaddingLeft(), v.getPaddingTop(),
                         v.getPaddingRight(), v.getPaddingBottom() + size);
            break;
        }
    }
}

private static int getNavigationBarSize(Resources resources) {
    int resourceId = resources.getIdentifier("navigation_bar_height",
                                             "dimen", "android");
    return resourceId > 0 ? resources.getDimensionPixelSize(resourceId) : 0;
}

private static boolean hasNavigationBar(Resources resources) {
    int hasNavBarId = resources.getIdentifier("config_showNavigationBar",
                                              "bool", "android");
    return hasNavBarId > 0 && resources.getBoolean(hasNavBarId);
}
Run Code Online (Sandbox Code Playgroud)