ANDROID-底部虚拟导航的高度获取
现在许多手机都引入了虚拟导航,所以在android开发的时候,经常需要对底部的虚拟导航进行适配。这篇文章主要是获取底部导航的高度,以及对导航是否显示进行判断。
如何获取手机底部虚拟导航的高度
本文所使用的方法是在实际项目中使用的,可能并不适合你的要求,所以仅供参考即可。
代码如下
//获取底部导航的高度
public static int getBottomStatusHeight(Context context) {
int totalHeight = getDpi(context);
int contentHeight = getScreenHeight(context);
PrintLog.printDebug(TAG, "--显示虚拟导航了--");
return totalHeight - contentHeight;
}
//获取屏幕原始尺寸高度,包括虚拟功能键高度
public static int getDpi(Context context) {
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 getScreenHeight(Context context) {
DisplayMetrics dm = context.getResources().getDisplayMetrics();
return dm.heightPixels;
}
在大部分手机上,该代码可以正常运行,当虚拟导航栏存在的时候,可以正确的显示底部导航高度,如果底部导航不存在 ,则会显示为0 。既然是在大部分手机上,那么,在部分手机上就是不正确的,比如华为手机。在华为P9上正常使用,后来换了个mate 20,把底部虚拟按键隐藏掉之后,发现页面底部空了一块,而这块的高度正好是虚拟按键的高度,但是此时手机的虚拟按键是隐藏状态,显然,这不是我们需要的结果。因此,如果光判断手机是否支持虚拟按键是不够的,我们还需要知道 当前手机的虚拟按键是否被隐藏了。也就是说:如果手机存在虚拟按键并且显示的情况下,我们才针对页面 和导航进行操作。所以,我们需要先判断一下虚拟导航是否显示 。
判断虚拟导航是否显示,针对华为手机,做了一些的特殊处理 代码如下:
/**
* 判断虚拟导航栏是否显示
*
* @param context 上下文对象
* @return true(显示虚拟导航栏),false(不显示或不支持虚拟导航栏)
*/
public static boolean checkNavigationBarShow(@NonNull Context context) {
boolean hasNavigationBar = false;
Resources rs = context.getResources();
int id = rs.getIdentifier("config_showNavigationBar", "bool", "android");
if (id > 0) {
hasNavigationBar = rs.getBoolean(id);
}
try {
Class systemPropertiesClass = Class.forName("android.os.SystemProperties");
Method m = systemPropertiesClass.getMethod("get", String.class);
String navBarOverride = (String) m.invoke(systemPropertiesClass, "qemu.hw.mainkeys");
//判断是否隐藏了底部虚拟导航
int navigationBarIsMin = 0;
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
navigationBarIsMin = Settings.System.getInt(context.getContentResolver(),
"navigationbar_is_min", 0);
} else {
navigationBarIsMin = Settings.Global.getInt(context.getContentResolver(),
"navigationbar_is_min", 0);
}
if ("1".equals(navBarOverride) || 1 == navigationBarIsMin) {
hasNavigationBar = false;
} else if ("0".equals(navBarOverride)) {
hasNavigationBar = true;
}
} catch (Exception e) {
}
return hasNavigationBar;
}
本段代码是整理之后的代码,仅供参考。
相信很多人在判断虚拟导航的时候都是使用了
int id = rs.getIdentifier(“config_showNavigationBar”, “bool”, “android”);
if (id > 0) {
hasNavigationBar = rs.getBoolean(id);
} 这段代码来判断的,对于大部分手机来说,基本上是可以实现需求的。但是在华为手机中,紧紧这样还是不够的。因为,我们引入了一个新的参数:navigationbar_is_min ;我们会根据navigationbar_is_min的数值,来判断当前虚拟按键是否显示。该方案通过监控settings数据库表中navigationbar_is_min的值,来判断当前是否显示了虚拟导航。我们可以看到,在代码中加有一个系统版本的判断。这是因为android5.0之后增加了多用户的特性,虚拟键盘的navigationbar_is_min字段从Settings.db的System表格移到了Global表。
因此:最后的获取软键盘高度的方法如下:
/**
* 获取 虚拟按键的高度
*
* @param context
* @return
*/
public static int getBottomStatusHeight(Context context) {
if (checkNavigationBarShow(context)) {
int totalHeight = getDpi(context);
int contentHeight = getScreenHeight(context);
PrintLog.printDebug(TAG, "--显示虚拟导航了--");
return totalHeight - contentHeight;
} else {
PrintLog.printDebug(TAG, "--没有虚拟导航 或者虚拟导航隐藏--");
return 0;
}
}
另外:如果需要对虚拟键盘是否显示进行监听的话,可以使用一下方法
private ContentObserver mNavigationBarObserver = new ContentObserver(new Handler()) {
@Override
public void onChange(boolean selfChange) {
int navigationBarIsMin = 0;
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP){
navigationBarIsMin = Settings.System.getInt(getContentResolver(),
"navigationbar_is_min", 0);
} else {
navigationBarIsMin = Settings.Global.getInt(getContentResolver(),
"navigationbar_is_min", 0);
}
Log.e(TAG, "onChange: " + navigationBarIsMin);
if (navigationBarIsMin == 1) {
//导航键隐藏了
} else {
//导航键显示了
}
}
};
另外在提供一种方法来判断软键盘是否显示 ,是通过监听页面布局的变化来实现的
FrameLayout content;
private boolean mLayoutComplete = false;
private int usableHeightPrevious;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
.......
content = (FrameLayout) findViewById(android.R.id.content);
content.post(new Runnable() {
@Override
public void run() {
mLayoutComplete = true;
Log.e(TAG, "content 布局完成");
}
});
content.getViewTreeObserver().addOnGlobalLayoutListener(this);
}
@Override
public void onGlobalLayout() {
if (!mLayoutComplete)
return;
boolean show = isNavigationBarShow(this);
}
public static boolean isNavigationBarShow(Activity activity){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
Display display = activity.getWindowManager().getDefaultDisplay();
Point size = new Point();
Point realSize = new Point();
display.getSize(size);
display.getRealSize(realSize);
return realSize.y!=size.y;
}else {
boolean menu = ViewConfiguration.get(activity).hasPermanentMenuKey();
boolean back = KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_BACK);
if(menu || back) {
return false;
}else {
return true;
}
}
}
至此:获取底部虚拟导航高度的方法已完毕,仅供参考。
//另外,记录一下获取状态栏高度的方法
/**
* 获取状态栏的高度
* @return
*/
public static int getStatusBarHeight(Context context) {
try {
Class<?> c = Class.forName("com.android.internal.R$dimen");
Object obj = c.newInstance();
Field field = c.getField("status_bar_height");
int x = Integer.parseInt(field.get(obj).toString());
return context.getResources().getDimensionPixelSize(x);
} catch (Exception e) {
e.printStackTrace();
}
return 0;
}
以上。