[Android] NavigationBar Height

Virtual NavigationBar Height In FullScreen Phone

Posted by xiuyuantech on 2020-05-23

Android 状态栏相关的文章网上一大堆,但是讲解导航栏的文章大多有系统兼容性问题。
本文将讲解如何准确获取导航栏高度,并且兼容性更好

先引用一下状态栏相关的网络图片
状态栏

现在大部分的手机都没有底部虚拟导航键了,
但是还是有些手机厂商依然在发布带虚拟导航键的手机。
既然有,开发者就得去适配,我遇到比较麻烦的一个问题是获取屏幕(除了虚拟导航键以外的内容显示区域)高度(content)。
当时想着网上查找资料,判断是否有导航键。但是没有找到有效的方法。
最后只能转变思路,通过debug、看源码来获取高度来判断是否有导航键。

布局

通过上面的图片大致可以知道 DecorView 里有三个子View分别是statusbar、content、navigationbar。
虽然每家厂商定制ui,navigationbar高度可能不同系统不同版本都会不一致。
但是整体框架上应该不会改动,总体布局上还是这个三个布局。
所以我们可以通过获取整个手机高度,再获取content的高度便可以获取navigationbar最终高度

状态栏

获取状态栏目前有两种方法

第一种:

1
2
3
4
5
6
7
8
9

public static int getStatusBarHeight(Context context) {
int result = 0;
int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
result = context.getResources().getDimensionPixelSize(resourceId);
}
return result;
}

第二种:

1
2
3
4
5
6
7
8

public static int getStatusBarHeight(Activity activity) {
View view = activity.getWindow().getDecorView();
Rect frame = new Rect();
activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(frame);
int statusBarHeight = frame.top;
return statusBarHeight;
}

虚拟导航栏

目前网上有许多获取虚拟导航栏的高度,但是大多数都会有些兼容性问题。

比如在OPPO、VIVO手机 上可以正确获取到高度,但在有些华为、小米部分机型上获取
到的高度有差别。下面先看下 错误 的代码

案例1:

1
2
3
4
5
6
7
8
9
10
11
12
13
14

public static int getRealHeight(Activity activity) {
Window window = activity.getWindow();
View decorView = window.getDecorView();
Configuration conf = activity.getResources().getConfiguration();
if (Configuration.ORIENTATION_LANDSCAPE == conf.orientation) {
View contentView = decorView.findViewById(android.R.id.content);
return contentView.getWidth();
} else {
Rect rect = new Rect();
decorView.getWindowVisibleDisplayFrame(rect);
return rect.bottom;
}
}

案例1 获取的屏幕可见部分的高,有的人就会那个这个高度减去statusbar的高度,这样截屏去掉
navigationbar高度后你会发现得到的图片会有问题

案例2:

1
2
3
4
5
6
7
8
9
10

public static int getNavigationBarHeight(Context context) {
int navigationBarHeight = -1;
Resources resources = context.getResources();
int resourceId = resources.getIdentifier("navigation_bar_height","dimen", "android");
if (resourceId > 0) {
navigationBarHeight = resources.getDimensionPixelSize(resourceId);
}
return navigationBarHeight;
}

案例2 这个代码是不是有点熟悉,和获取statusbar高度第一种方法差不多。
获取statusbar高度第一种方法目前是没有发现兼容性问题,而navigationbar案例2却存在这样
的问题,生产环境不可用。

其他方法就不一一列举了,网上资料一大堆,下面讲解下正确的做法。

总结

作为Android开发工程师大家都应该知道获取content的方法吧,在这个我就不贴
代码了,不知道的自己去查吧。根据上面我讲的布局逻辑,我们可以得出这样一个结论:
navigationbar的高度 = window的高度 - statusbar高度 - content的高度
也等于
navigationbar的高度 = window的高度 - content的bottom
不理解View的Top,Left,Right,Bottom属性的自行查阅资料。其中最关键的代码我在
[Android] MediaProjection 也提到过都是重点

正确代码:

1
2
3
4
5
6
7
8
9
10
public static int getNavigationBarRealHeight(Activity activity) {
Window window = activity.getWindow();
View decorView = window.getDecorView();
Rect rect = new Rect();
decorView.getWindowVisibleDisplayFrame(rect);
DisplayMetrics outMetrics = new DisplayMetrics();
WindowManager windowManager = window.getWindowManager();
windowManager.getDefaultDisplay().getRealMetrics(outMetrics);
return outMetrics.heightPixels - rect.bottom;
}