如何以编程方式获取Android导航栏的高度和宽度?
Android的屏幕底部的黑色导航栏不容易移动。 自3.0以来,它一直是Android的一部分,作为硬件button的替代品。 这是一张图片:
我怎样才能得到这个UI元素的宽度和高度的像素的大小?
尝试下面的代码:
Resources resources = context.getResources(); int resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android"); if (resourceId > 0) { return resources.getDimensionPixelSize(resourceId); } return 0;
我通过比较可用屏幕尺寸和实际屏幕尺寸来获得导航栏大小。 当应用程序可用的屏幕尺寸小于实际屏幕尺寸时,我假定导航栏存在。 然后我计算导航栏的大小。 此方法适用于API 14及更高版本。
public static Point getNavigationBarSize(Context context) { Point appUsableSize = getAppUsableScreenSize(context); Point realScreenSize = getRealScreenSize(context); // navigation bar on the right 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; }
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; }
实际上,平板电脑上的导航栏(至lessNexus 7)在纵向和横向上的大小不同,所以此function应如下所示:
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; }
我希望这可以帮助你
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; }
这是我的代码将paddingRight和paddingBottom添加到视图以闪避导航栏。 我在这里结合了一些答案,并与isInMultiWindowMode一起为横向定义了一个特殊的子句。 关键是阅读navigation_bar_height ,但也检查config_showNavigationBar ,以确保我们应该实际使用的高度。
以前的解决scheme没有为我工作。 从Android 7.0开始,您必须考虑多窗口模式。 这将打破比较display.realSize与display.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); }
底部导航栏的高度为48dp(在纵向和横向模式下),垂直放置时为42dp。
由Egidijus提出的解决scheme,完美适用于Build.VERSION.SDK_INT> = 17
但是,在我的设备上使用Build.VERSION.SDK_INT <17执行以下语句时出现“NoSuchMethodException”
Display.class.getMethod("getRawHeight").invoke(display);
我已经修改了方法getRealScreenSize()这种情况下:
else if(Build.VERSION.SDK_INT >= 14) { View decorView = getActivity().getWindow().getDecorView(); size.x = decorView.getWidth(); size.y = decorView.getHeight(); }
我解决了所有设备(包括Nexus 5,三星Galaxy Nexus 6 edge +,三星S10,三星Note II等)的这个问题。 我认为这将帮助您处理设备相关的问题。
这里我添加了两种types的代码,
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; } }
和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; }
这是我如何解决这个问题。 我做了一个需要填充的可隐藏底栏,这取决于是否有导航栏(电容,屏幕上或只是棒棒糖)。
视图
setPadding(0, 0, 0, Utils.hasNavBar(getContext()) ? 30 : 0);
Utils.java
public static boolean hasNavBar(Context context) { // Kitkat and less shows container above nav bar if (android.os.Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) { return false; } // Emulator if (Build.FINGERPRINT.startsWith("generic")) { return true; } boolean hasMenuKey = ViewConfiguration.get(context).hasPermanentMenuKey(); boolean hasBackKey = KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_BACK); boolean hasNoCapacitiveKeys = !hasMenuKey && !hasBackKey; Resources resources = context.getResources(); int id = resources.getIdentifier("config_showNavigationBar", "bool", "android"); boolean hasOnScreenNavBar = id > 0 && resources.getBoolean(id); return hasOnScreenNavBar || hasNoCapacitiveKeys || getNavigationBarHeight(context, true) > 0; } public static int getNavigationBarHeight(Context context, boolean skipRequirement) { int resourceId = context.getResources().getIdentifier("navigation_bar_height", "dimen", "android"); if (resourceId > 0 && (skipRequirement || hasNavBar(context))) { return context.getResources().getDimensionPixelSize(resourceId); } return 0; }