如何以编程方式获取 Android 导航栏的高度和宽度?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/20264268/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do I get the height and width of the Android Navigation Bar programmatically?
提问by Kevik
The black navigation bar on the bottom of the screen is not easily removable in Android. It has been part of Android since 3.0 as a replacement for hardware buttons. Here is a picture:
屏幕底部的黑色导航栏在 Android 中不容易移除。自 3.0 以来,它一直是 Android 的一部分,作为硬件按钮的替代品。这是一张图片:
How can I get the size of the width and the height of this UI element in pixels?
如何以像素为单位获取此 UI 元素的宽度和高度的大小?
回答by Sanket
Try below code:
试试下面的代码:
Resources resources = context.getResources();
int resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android");
if (resourceId > 0) {
return resources.getDimensionPixelSize(resourceId);
}
return 0;
回答by Egis
I get navigation bar size by comparing app-usable screen size with real screen size. I assume that navigation bar is present when app-usable screen size is smaller than real screen size. Then I calculate navigation bar size. This method works with API 14 and up.
我通过将应用程序可用的屏幕尺寸与实际屏幕尺寸进行比较来获得导航栏尺寸。我假设当应用程序可用的屏幕尺寸小于实际屏幕尺寸时存在导航栏。然后我计算导航栏的大小。此方法适用于 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;
}
UPDATE
更新
For a solution that takes into account display cutouts please check John's answer.
有关考虑显示切口的解决方案,请查看John 的回答。
回答by Mdlc
The NavigationBar height varies for some devices, but as well for some orientations. First you have to check if the device has a navbar, then if the device is a tablet or a not-tablet (phone) and finally you have to look at the orientation of the device in order to get the correct height.
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;
}
回答by Danylo Volokh
Actually the navigation bar on tablets (at least Nexus 7) has different size in portrait and landscape so this function should look like this:
实际上,平板电脑(至少 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;
}
回答by John
I think better answer is here because it allows you to get even cutout height too.
我认为更好的答案在这里,因为它也可以让您获得均匀的切口高度。
Take your root view, and add setOnApplyWindowInsetsListener (or you can override onApplyWindowInsets from it), and take insets.getSystemWindowInsets from it.
获取您的根视图,并添加 setOnApplyWindowInsetsListener(或者您可以从中覆盖 onApplyWindowInsets),并从中获取 insets.getSystemWindowInsets。
In my camera activity, i add padding equal to the systemWindowInsetBottom to my bottom layout. And finally, it fix cutout issue.
在我的相机活动中,我将等于 systemWindowInsetBottom 的填充添加到我的底部布局。最后,它修复了切口问题。
with appcompat it is like this
与 appcompat 是这样的
ViewCompat.setOnApplyWindowInsetsListener(mCameraSourcePreview, (v, insets) -> {
takePictureLayout.setPadding(0,0,0,insets.getSystemWindowInsetBottom());
return insets.consumeSystemWindowInsets();
});
without appcompat, this:
没有appcompat,这个:
mCameraSourcePreview.setOnApplyWindowInsetsListener((v, insets) -> { ... })
回答by Francisco Drumond
I hope this helps you
我希望这可以帮助你
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;
}
回答by marcus
This is my code to add paddingRight and paddingBottom to a View to dodge the Navigation Bar. I combined some of the answers here and made a special clause for landscape orientation together with isInMultiWindowMode. The key is to read navigation_bar_height, but also check config_showNavigationBarto make sure we should actually use the height.
这是我将 paddingRight 和 paddingBottom 添加到视图以避开导航栏的代码。我在这里结合了一些答案,并与 isInMultiWindowMode 一起制定了横向方向的特殊条款。关键是阅读navigation_bar_height,还要检查config_showNavigationBar以确保我们应该实际使用高度。
None of the previous solutions worked for me. As of Android 7.0 you have to take Multi Window Mode into consideration.This breaks the implementations comparing display.realSizewith display.sizesince realSizegives you the dimensions of the whole screen (both split windows) and sizeonly gives you the dimensions of your App window. Setting padding to this difference will leave your whole view being padding.
以前的解决方案都不适合我。从 Android 7.0 开始,您必须考虑多窗口模式。这打破了比较display.realSize和display.size的实现,因为realSize为您提供整个屏幕(两个拆分窗口)的尺寸,而size只为您提供应用程序窗口的尺寸。将填充设置为这种差异将使您的整个视图处于填充状态。
/** 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);
}
回答by Meanman
Combining the answer from @egis and others - this works well on a variety of devices, tested on Pixel EMU, Samsung S6, Sony Z3, Nexus 4. This code uses the display dimensions to test for availability of nav bar and then uses the actual system nav bar size if present.
结合@egis 和其他人的答案 - 这适用于各种设备,在 Pixel EMU、Samsung S6、Sony Z3、Nexus 4 上进行测试。此代码使用显示尺寸来测试导航栏的可用性,然后使用实际系统导航栏大小(如果存在)。
/**
* Calculates the system navigation bar size.
*/
public final class NavigationBarSize {
private final int systemNavBarHeight;
@NonNull
private final Point navBarSize;
public NavigationBarSize(@NonNull Context context) {
Resources resources = context.getResources();
int displayOrientation = resources.getConfiguration().orientation;
final String name;
switch (displayOrientation) {
case Configuration.ORIENTATION_PORTRAIT:
name = "navigation_bar_height";
break;
default:
name = "navigation_bar_height_landscape";
}
int id = resources.getIdentifier(name, "dimen", "android");
systemNavBarHeight = id > 0 ? resources.getDimensionPixelSize(id) : 0;
navBarSize = getNavigationBarSize(context);
}
public void adjustBottomPadding(@NonNull View view, @DimenRes int defaultHeight) {
int height = 0;
if (navBarSize.y > 0) {
// the device has a nav bar, get the correct size from the system
height = systemNavBarHeight;
}
if (height == 0) {
// fallback to default
height = view.getContext().getResources().getDimensionPixelSize(defaultHeight);
}
view.setPadding(0, 0, 0, height);
}
@NonNull
private static Point getNavigationBarSize(@NonNull Context context) {
Point appUsableSize = new Point();
Point realScreenSize = new Point();
WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
if (windowManager != null) {
Display display = windowManager.getDefaultDisplay();
display.getSize(appUsableSize);
display.getRealSize(realScreenSize);
}
return new Point(realScreenSize.x - appUsableSize.x, realScreenSize.y - appUsableSize.y);
}
}
回答by Anh Duy
How to get the height of the navigation bar and status bar. This code works for me on some Huawei devices and Samsung devices. Egis's solution above is good, however, it is still incorrect on some devices. So, I improved it.
如何获取导航栏和状态栏的高度。此代码适用于某些华为设备和三星设备。上面Egis的解决方案是好的,但在某些设备上仍然不正确。所以,我改进了它。
This is code to get the height of status bar
这是获取状态栏高度的代码
private fun getStatusBarHeight(resources: Resources): Int {
var result = 0
val resourceId = resources.getIdentifier("status_bar_height", "dimen", "android")
if (resourceId > 0) {
result = resources.getDimensionPixelSize(resourceId)
}
return result
}
This method always returns the height of navigation bar even when the navigation bar is hidden.
即使导航栏被隐藏,此方法也始终返回导航栏的高度。
private fun getNavigationBarHeight(resources: Resources): Int {
val resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android")
return if (resourceId > 0) {
resources.getDimensionPixelSize(resourceId)
} else 0
}
NOTE: on Samsung A70, this method returns the height of the status bar + height of the navigation bar. On other devices (Huawei), it only returns the height of the Navigation bar and returns 0 when the navigation bar is hidden.
注意:在三星 A70 上,此方法返回状态栏的高度 + 导航栏的高度。在其他设备(华为)上,只返回导航栏的高度,隐藏导航栏时返回0。
private fun getNavigationBarHeight(): Int {
val display = activity?.windowManager?.defaultDisplay
return if (display == null) {
0
} else {
val realMetrics = DisplayMetrics()
display.getRealMetrics(realMetrics)
val metrics = DisplayMetrics()
display.getMetrics(metrics)
realMetrics.heightPixels - metrics.heightPixels
}
}
This is code to get height of navigation bar and status bar
这是获取导航栏和状态栏高度的代码
val metrics = DisplayMetrics()
activity?.windowManager?.defaultDisplay?.getRealMetrics(metrics)
//resources is got from activity
//NOTE: on SamSung A70, this height = height of status bar + height of Navigation bar
//On other devices (Huawei), this height = height of Navigation bar
val navigationBarHeightOrNavigationBarPlusStatusBarHeight = getNavigationBarHeight()
val statusBarHeight = getStatusBarHeight(resources)
//The method will always return the height of navigation bar even when the navigation bar was hidden.
val realNavigationBarHeight = getNavigationBarHeight(resources)
val realHeightOfStatusBarAndNavigationBar =
if (navigationBarHeightOrNavigationBarPlusStatusBarHeight == 0 || navigationBarHeightOrNavigationBarPlusStatusBarHeight < statusBarHeight) {
//Huawei: navigation bar is hidden
statusBarHeight
} else if (navigationBarHeightOrNavigationBarPlusStatusBarHeight == realNavigationBarHeight) {
//Huawei: navigation bar is visible
statusBarHeight + realNavigationBarHeight
} else if (navigationBarHeightOrNavigationBarPlusStatusBarHeight < realNavigationBarHeight) {
//SamSung A70: navigation bar is still visible but it only displays as a under line
//navigationBarHeightOrNavigationBarPlusStatusBarHeight = navigationBarHeight'(under line) + statusBarHeight
navigationBarHeightOrNavigationBarPlusStatusBarHeight
} else {
//SamSung A70: navigation bar is visible
//navigationBarHeightOrNavigationBarPlusStatusBarHeight == statusBarHeight + realNavigationBarHeight
navigationBarHeightOrNavigationBarPlusStatusBarHeight
}
回答by Sino Raj
I resolved this issue for all devices(including Nexus 5, Samsung Galaxy Nexus 6 edge+, Samsung S10, Samsung Note II etc.). I think this will help you to handle device dependant issues.
我为所有设备(包括 Nexus 5、Samsung Galaxy Nexus 6 edge+、Samsung S10、Samsung Note II 等)解决了这个问题。我认为这将帮助您处理设备相关问题。
Here I am adding two types of codes,
在这里,我添加了两种类型的代码,
Java Code(for Native Android):
Java 代码(适用于原生 Android):
import android.content.Context;
import android.content.res.Resources;
import android.os.Build;
import android.util.DisplayMetrics;
import android.view.Display;
import android.view.ViewConfiguration;
import android.view.WindowManager;
public class DeviceSpec {
private int resourceID = -1;
private Display display = null;
private DisplayMetrics displayMetrics = null;
private DisplayMetrics realDisplayMetrics = null;
private Resources resources = null;
private WindowManager windowManager = null;
public double GetNavigationBarHeight(Context context) {
try {
windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
display = windowManager.getDefaultDisplay();
displayMetrics = new DisplayMetrics();
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
realDisplayMetrics = new DisplayMetrics();
display.getMetrics(displayMetrics);
display.getRealMetrics(realDisplayMetrics);
if(displayMetrics.heightPixels != realDisplayMetrics.heightPixels) {
resources = context.getResources();
return GetNavigationBarSize(context);
}
}
else {
resources = context.getResources();
resourceID = resources.getIdentifier("config_showNavigationBar", "bool", "android");
if (resourceID > 0 && resources.getBoolean(resourceID))
return GetNavigationBarSize(context);
}
}
catch (Exception e){
e.printStackTrace();
}
return 0;
}
private double GetNavigationBarSize(Context context) {
resourceID = resources.getIdentifier("navigation_bar_height", "dimen", "android");
if (resourceID > 0 && ViewConfiguration.get(context).hasPermanentMenuKey())
return (resources.getDimensionPixelSize(resourceID) / displayMetrics.density);
return 0;
}
}
And C# code(for Xamarin Forms/Android)
和 C# 代码(用于 Xamarin Forms/Android)
int resourceId = -1;
IWindowManager windowManager = null;
Display defaultDisplay = null;
DisplayMetrics displayMatrics = null;
DisplayMetrics realMatrics = null;
Resources resources = null;
public double NavigationBarHeight
{
get
{
try
{
windowManager = Forms.Context.GetSystemService(Context.WindowService).JavaCast<IWindowManager>();
defaultDisplay = windowManager.DefaultDisplay;
displayMatrics = new DisplayMetrics();
if (Build.VERSION.SdkInt >= BuildVersionCodes.JellyBeanMr2)
{
realMatrics = new DisplayMetrics();
defaultDisplay.GetMetrics(displayMatrics);
defaultDisplay.GetRealMetrics(realMatrics);
if (displayMatrics.HeightPixels != realMatrics.HeightPixels)
{
resources = Forms.Context.Resources;
return GetHeightOfNivigationBar();
}
}
else {
resources = Forms.Context.Resources;
resourceId = resources.GetIdentifier("config_showNavigationBar", "bool", "android");
if (resourceId > 0 && resources.GetBoolean(resourceId))
return GetHeightOfNivigationBar();
}
}
catch (Exception e) { }
return 0;
}
}
private double GetHeightOfNivigationBar()
{
resourceId = resources.GetIdentifier("navigation_bar_height", "dimen", "android");
if (!ViewConfiguration.Get(Forms.Context).HasPermanentMenuKey && resourceId > 0)
{
return resources.GetDimensionPixelSize(resourceId) / displayMatrics.Density;
}
return 0;
}