ANDROID-底部虚拟导航的高度获取

6,340 阅读4分钟

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;
    }

以上。

参考文档:www.jianshu.com/p/0f23fd29f…