虛擬導航欄也是一個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;
}
複製代碼這種方法是判斷系統窗口占用區域,底部可能出現的系統窗口除了虛擬導航欄,可能還存在虛擬鍵盤,似乎不太好判斷,但是由於我們可以得到系統配置的虛擬導航欄的高度,所以在這些系統佔用的窗口高度中我們可以篩選出虛擬導航欄的高度。因此,總的來講,這種判斷也是很不錯的。