我正在尝试检查加载时是否存在android导航栏,以便我可以相应地调整布局,有人有什么建议吗?
这是我要检测的导航栏:
PS 到目前为止,我发现的所有方法都是尝试删除栏的“坏”方法,我不想这样做。
我正在尝试检查加载时是否存在android导航栏,以便我可以相应地调整布局,有人有什么建议吗?
这是我要检测的导航栏:
PS 到目前为止,我发现的所有方法都是尝试删除栏的“坏”方法,我不想这样做。
花了我一些时间,但我找到了一种比依赖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
}
没有可靠的方法来检查导航栏。使用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
错误。
另一个解决方案(我的类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);
}
这是一个结合 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;
}
}
我已经这样做了,它适用于我测试的每台设备,甚至在模拟器上:
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());
}
您可以将此代码添加到活动的 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.
}
}
});
这种方法对我有用
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
}
它对我有用,并在许多设备上进行了测试。
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;
}
}
可能更好的方法是测量屏幕。
从 API 17 开始,有getWindowManager().getDefaultDisplay().getRealSize()
,可以与返回的大小进行比较getWindowManager().getDefaultDisplay().getSize()
。
如果你得到不同的结果,我认为可以肯定地说有一个导航栏,如果你得到相同的结果,就没有导航栏。需要注意的一件事是您的目标 SDK 和支持的屏幕,getSize()
如果 Android 认为您的应用在不缩放的情况下无法在当前设备上正常运行,这可能会导致结果被缩放。
getWindowManager().getDefaultDisplay().getMetrics()
在 API 17 以下,您可以通过横向和纵向模式测量屏幕,同样,不同的结果可能意味着有一个导航栏。
但是,如果您获得相同的结果,您实际上并不知道,因为即使在横向时,手机也可以将导航栏保持在较短的边缘。有根据的猜测是,如果宽度或高度比标准尺寸(如1280x800
, 1280x720
, )小 4% 到 8% 1024x600
,而另一个尺寸相等,那么可能还有一个导航栏。不过,不要赌它。有太多的分辨率,它们之间的差异太小,无法正常工作。
我看到上面的答案,我想表明“不存在”可以视为高度为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;
}
```
在 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 (来源)也使用了这个解决方案,这就是我了解它的方式。
解决方案:只有没有永久硬件密钥的设备才有导航栏,因此您可以检查 API 版本并使用 hasPermanentMenuKey() 查找硬件密钥
boolean hasMenuKey = ViewConfiguration.get(context).hasPermanentMenuKey();