在Android中,屏幕底部的黑色导航栏不容易删除。自从3.0版本以来,它已经成为 Android 的一部分,用于替换硬件按钮。这里是一张图片:
我如何获取该UI元素的宽度和高度(以像素为单位)?
在Android中,屏幕底部的黑色导航栏不容易删除。自从3.0版本以来,它已经成为 Android 的一部分,用于替换硬件按钮。这里是一张图片:
我如何获取该UI元素的宽度和高度(以像素为单位)?
请尝试以下代码:
Resources resources = context.getResources();
int resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android");
if (resourceId > 0) {
return resources.getDimensionPixelSize(resourceId);
}
return 0;
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 - user149408我通过比较应用可用屏幕尺寸和实际屏幕尺寸来获取导航栏大小。当应用可用屏幕尺寸小于实际屏幕尺寸时,我假设导航栏存在。然后我计算导航栏的大小。这种方法适用于API14及以上。
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;
}
更新
针对考虑显示切口的解决方案,请查看John的答案。
NavigationBar的高度因设备而异,也因方向而异。首先你需要检查设备是否有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;
}
hasBackKey = false!
,尽管它应该是true。boolean navBarExists = getResources().getBoolean(getResources().getIdentifier("config_showNavigationBar", "bool", "android"));
- Hamzeh Soboh实际上,在平板电脑上(至少是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;
}
而在 Kotlin 中:
private fun getNavigationBarHeight(): Int {
val resources: Resources = requireContext().resources
val resName = if (resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT) {
"navigation_bar_height"
} else {
"navigation_bar_height_landscape"
}
val id: Int = resources.getIdentifier(resName, "dimen", "android")
return if (id > 0) {
resources.getDimensionPixelSize(id)
} else {
0
}
}
context.getResources().getConfiguration().orientation
。 - Hugo Gresse我认为这里的答案更好,因为它允许你获得切除高度。
获取你的根视图,并添加setOnApplyWindowInsetsListener(或者你可以重写onApplyWindowInsets),从中获取插入值。
在我的相机活动中,我将系统栏的底部填充到我的底部布局。最后,它修复了切口问题。
使用appcompat的话就像这样:
ViewCompat.setOnApplyWindowInsetsListener(binding.root) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
binding.takePictureLayout.apply {
setPaddingRelative(paddingStart, paddingTop, paddingEnd, systemBars.bottom)
}
return@setOnApplyWindowInsetsListener insets
}
没有 appcompat,这样做:
mCameraSourcePreview.setOnApplyWindowInsetsListener((v, insets) -> { ... })
setOnApplyWindowInsetsListener
吗?如果可以,如何使用?为什么要区分LOLLIPOP和其他版本? - android developer我希望这可以帮助你。
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;
}
受到Egis的回答的启发:
context.navigationBarHeight
扩展名获取器所在的位置
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
}
}
测试环境:
/** 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);
}
获取导航栏高度的测试代码(以像素为单位):
public static int getNavBarHeight(Context c) {
int resourceId = c.getResources()
.getIdentifier("navigation_bar_height", "dimen", "android");
if (resourceId > 0) {
return c.getResources().getDimensionPixelSize(resourceId);
}
return 0;
}
获取状态栏高度(以像素为单位)的测试代码:
public static int getStatusBarHeight(Context c) {
int resourceId = c.getResources()
.getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
return c.getResources().getDimensionPixelSize(resourceId);
}
return 0;
}
像素转换为dp:
public static int pxToDp(int px) {
return (int) (px / Resources.getSystem().getDisplayMetrics().density);
}
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;
}
Display.getRealMetrics()
需要 API 级别 17。 - WeizhiscreenOrientation
为默认值时,只有在不旋转时hasPermanentMenuKey
为false
。
[2] 当screenOrientation
为横向时,它会落入displayMetrics.heightPixels!= realDisplayMetrics.heightPixels)
的else中。
[3] 当screenOrientation
为纵向时,hasPermanentMenuKey
为false
。 - 林果皞