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;@Overrideprotected 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);}@Overridepublic 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; }复制代码
以上。
参考文档: