当我们需要确定设备是否存在虚拟导航栏时,可以借助 Android 系统层面的技术来实现。虚拟导航栏本质上也是一个 View,在布局中绘制并显示在窗口布局中。因此,我们可以通过分析布局层级找到代表虚拟导航栏的 View,并验证其是否存在来判断虚拟导航栏的存在。
首先,我们可以使用 Android Studio 提供的 Layout Inspector 工具来分析应用的布局层级。通常情况下,虚拟导航栏的 View 是 DecorView 的子 View(在 Android 5.0 以上的版本中是这样)。在 DecorView 中找到代表虚拟导航栏的 View 后,我们就可以继续验证其是否存在。
以下是通过代码实现这一过程的示例:
private static final String NAVIGATION = "navigationBarBackground";
/**
* 判断设备是否存在导航栏。
* 注意:此方法需要在视图完全绘制出来之后调用,否则无法准确判断。
* 例如,在 onWindowFocusChanged() 方法中调用可以得到正确的结果。
*
* @param activity 当前活动
* @return 如果存在导航栏则返回 true,否则返回 false
*/
public static boolean isNavigationBarExist(@NonNull Activity activity) {
// 获取窗口的 DecorView
ViewGroup vp = (ViewGroup) activity.getWindow().getDecorView();
// 遍历 DecorView 的子 View,寻找导航栏的 View
if (vp != null) {
for (int i = 0; i < vp.getChildCount(); i++) {
// 获取当前子 View
View childView = vp.getChildAt(i);
// 判断当前子 View 是否是导航栏的背景 View
if (childView.getId() != View.NO_ID && NAVIGATION.equals(activity.getResources().getResourceEntryName(childView.getId()))) {
return true; // 如果是导航栏背景 View,则表示存在导航栏
}
}
}
return false; // 未找到导航栏背景 View,表示不存在导航栏
}
还有另一种方法:
/**
* 判断设备是否存在导航栏,并回调导航栏状态。
* 注意:此方法需要在视图完全绘制出来之后调用,否则无法准确判断。
*
* @param activity 当前活动
* @param onNavigationStateListener 导航栏状态监听器
*/
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) {
// 设置窗口 DecorView 的 OnApplyWindowInsetsListener,监听窗口变化
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;
}
});
}
}
/**
* 获取导航栏的高度。
*
* @param activity 上下文
* @return 导航栏高度,单位为像素
*/
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) {
// 获取导航栏高度
height = resources.getDimensionPixelSize(resourceId);
}
return height;
}
/**
* 导航栏状态监听器接口。
*/
public interface OnNavigationStateListener {
/**
* 导航栏状态回调方法。
*
* @param isShowing 导航栏是否显示
* @param height 导航栏高度
*/
void onNavigationState(boolean isShowing, int height);
}