虚拟导航栏也是一个View,如果这个View绘制了自己,并显示在Window布局中,那么虚拟导航栏就一定存在。也就是说,我们只要找到这个View,并证明它是否存在即可。
于是我们尝试通过Layout Inspector分析了虚拟导航栏的布局层级,发现它是DecorView的Child View(Android5.0以上是这样),同时我们在DecorView中找到了代表虚拟导航栏的View,那么,接下来的问题就很简单了咯。代码如下:
{
private static final String NAVIGATION= "navigationBarBackground";
// 该方法需要在View完全被绘制出来之后调用,否则判断不了
//在比如 onWindowFocusChanged()方法中可以得到正确的结果
public static boolean isNavigationBarExist(@NonNull Activity activity){
ViewGroup vp = (ViewGroup) activity.getWindow().getDecorView();
if (vp != null) {
for (int i = 0; i < vp.getChildCount(); i++) {
vp.getChildAt(i).getContext().getPackageName();
if (vp.getChildAt(i).getId()!= NO_ID && NAVIGATION.equals(activity.getResources().getResourceEntryName(vp.getChildAt(i).getId()))) {
return true;
}
}
}
return false;
}
}
复制代码当然,还有一种判断方案,也很好。
public static void isNavigationBarExist(Activity activity, final OnNavigationStateListener onNavigationStateListener) {
if (activity == null) {
return;
}
final int height = getNavigationHeight(activity);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) {
activity.getWindow().getDecorView().setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() {
@Override
public WindowInsets onApplyWindowInsets(View v, WindowInsets windowInsets) {
boolean isShowing = false;
int b = 0;
if (windowInsets != null) {
b = windowInsets.getSystemWindowInsetBottom();
isShowing = (b == height);
}
if (onNavigationStateListener != null && b <= height) {
onNavigationStateListener.onNavigationState(isShowing, b);
}
return windowInsets;
}
});
}
}
public static int getNavigationHeight(Context activity) {
if (activity == null) {
return 0;
}
Resources resources = activity.getResources();
int resourceId = resources.getIdentifier("navigation_bar_height",
"dimen", "android");
int height = 0;
if (resourceId > 0) {
//获取NavigationBar的高度
height = resources.getDimensionPixelSize(resourceId);
}
return height;
}
复制代码这种方法是判断系统窗口占用区域,底部可能出现的系统窗口除了虚拟导航栏,可能还存在虚拟键盘,似乎不太好判断,但是由于我们可以得到系统配置的虚拟导航栏的高度,所以在这些系统占用的窗口高度中我们可以筛选出虚拟导航栏的高度。因此,总的来讲,这种判断也是很不错的。