android 如何判定底部导航栏显示时 不是键盘显示

在 Android 中判定底部导航栏是否显示时,核心痛点是 区分 “导航栏的底部 Insets” 和 “软键盘弹出的底部 Insets”—— 两者都会导致 getSystemWindowInsetBottom() 返回非零值,直接判断会误将键盘弹出当成导航栏显示。以下是基于 WindowInsets 类型区分 的精准解决方案,兼容不同 Android 版本和场景。

核心原理:通过 Insets 类型过滤键盘

Android 的 WindowInsets 会标记不同来源的 “插入区域”(如导航栏、状态栏、软键盘),通过 WindowInsetsCompat.Type 可精准过滤出 仅由导航栏贡献的底部 Insets,从而排除键盘干扰。

关键类型说明:

Insets 类型含义需排除 / 保留
Type.NAVIGATION_BARS系统导航栏(底部 / 侧边)保留(目标判断对象)
Type.IME软键盘(Input Method Editor)排除(干扰项)
Type.STATUS_BARS状态栏(顶部)排除(与底部无关)

方案实现:兼容高低版本的工具类

以下工具类支持 Android 14(API 34)及以下版本,通过 WindowInsetsCompat 统一处理 Insets 类型,精准判断导航栏可见性并获取高度。

import android.view.View;
import androidx.core.view.WindowInsetsCompat;/*** 精准判断底部导航栏是否显示(排除软键盘干扰)*/
public class NavigationBarChecker {/*** 判定底部导航栏当前是否可见(排除键盘)* @param rootView 页面根布局(如 Activity 的 contentView、Fragment 的根View)* @return true:导航栏显示;false:导航栏隐藏或当前是键盘弹出*/public static boolean isNavigationBarVisible(View rootView) {if (rootView == null) {return false;}// 1. 获取根View的WindowInsets(包含所有插入区域信息)WindowInsetsCompat insetsCompat = ViewCompat.getRootWindowInsets(rootView);if (insetsCompat == null) {return false; // 极端情况(如View未附着到窗口),返回隐藏}// 2. 关键:仅获取“导航栏”贡献的底部Insets(排除键盘、状态栏等)// Type.NAVIGATION_BARS:指定只计算导航栏的Insetsint navBarBottomInset = insetsCompat.getInsets(WindowInsetsCompat.Type.navigationBars()).bottom;// 3. 底部Insets > 0 说明导航栏在底部显示(若为侧边导航栏,bottom会是0,需额外判断left/right)return navBarBottomInset > 0;}/*** 获取底部导航栏的真实高度(排除键盘干扰)* @param rootView 页面根布局* @return 导航栏高度(px);0:导航栏隐藏*/public static int getNavigationBarHeight(View rootView) {if (rootView == null) {return 0;}WindowInsetsCompat insetsCompat = ViewCompat.getRootWindowInsets(rootView);if (insetsCompat == null) {return 0;}// 同样只取导航栏的底部Insets,即为导航栏高度return insetsCompat.getInsets(WindowInsetsCompat.Type.navigationBars()).bottom;}/*** (扩展)判断是否为软键盘弹出状态(辅助验证)* @param rootView 页面根布局* @return true:键盘显示;false:键盘隐藏*/public static boolean isKeyboardVisible(View rootView) {if (rootView == null) {return false;}WindowInsetsCompat insetsCompat = ViewCompat.getRootWindowInsets(rootView);if (insetsCompat == null) {return false;}// 仅判断“键盘”贡献的底部Insets:>0 说明键盘弹出//对core 版本有要求,太低找不到//dependencies {//implementation 'androidx.core:core:1.5.0'//}int keyboardBottomInset = insetsCompat.getInsets(WindowInsetsCompat.Type.ime()).bottom;return keyboardBottomInset > 0;}
}

关键细节说明

1. 为什么必须用 ViewCompat.getRootWindowInsets()
  • 避免直接调用 rootView.getRootWindowInsets():该方法在 API 23(Android 6.0)以上才可用,ViewCompat 会自动兼容低版本(API 14+),无需额外版本判断。
  • 确保获取的是 “根 View 的 Insets”:只有根布局(如 setContentView 传入的 View)能拿到完整的系统 Insets,子 View 可能因布局嵌套导致 Insets 被截断。
2. 如何处理 “侧边导航栏”(如平板横屏)?

部分设备(平板、折叠屏)在横屏时会将导航栏放在左侧 / 右侧,此时 bottom Insets 为 0,需额外判断 left 或 right

// 扩展:判断任意位置的导航栏是否可见(含侧边)
public static boolean isAnyNavigationBarVisible(View rootView) {if (rootView == null) return false;WindowInsetsCompat insetsCompat = ViewCompat.getRootWindowInsets(rootView);if (insetsCompat == null) return false;WindowInsetsCompat.Insets navInsets = insetsCompat.getInsets(WindowInsetsCompat.Type.navigationBars());// 左/右/下 任意一个方向有Insets,说明导航栏可见return navInsets.left > 0 || navInsets.right > 0 || navInsets.bottom > 0;
}
3. 兼容 Android 14(API 34)的新变化

Android 14 新增了 WindowInsets.Type.systemBars()(包含状态栏 + 导航栏),但 Type.navigationBars() 仍完全兼容,无需修改代码 ——WindowInsetsCompat 已内部适配新 API,保证低版本行为一致。

使用示例(在 Activity 中)

public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);// 获取页面根布局(必须是setContentView的根View)View rootView = findViewById(android.R.id.content); // 通用获取根View的方式// 1. 监听导航栏可见性变化(如键盘弹出/收起、旋转屏幕时)ViewCompat.setOnApplyWindowInsetsListener(rootView, (v, insets) -> {// 判断导航栏是否显示(排除键盘)boolean isNavVisible = NavigationBarChecker.isNavigationBarVisible(rootView);// 获取导航栏高度int navHeight = NavigationBarChecker.getNavigationBarHeight(rootView);// 判断键盘是否显示(辅助)boolean isKeyboard = NavigationBarChecker.isKeyboardVisible(rootView);// 业务逻辑:如更新UI、调整布局Log.d("NavChecker", "导航栏可见:" + isNavVisible + ",高度:" + navHeight + "px,键盘可见:" + isKeyboard);return insets; // 必须返回Insets,否则后续监听会失效});// 2. 主动触发一次判断(如页面初始化时)boolean initNavVisible = NavigationBarChecker.isNavigationBarVisible(rootView);int initNavHeight = NavigationBarChecker.getNavigationBarHeight(rootView);}
}

常见问题排查

  1. 返回值始终为 0?

    • 检查 rootView 是否为页面根布局(如用 findViewById(android.R.id.content) 替代子 View)。
    • 确保布局未设置 fitsSystemWindows="true":该属性会让 View 消费 Insets,导致 getInsets() 返回 0(如需使用,需在根 View 的父布局设置)。
  2. 键盘弹出时误判为导航栏?

    • 确认代码中使用 WindowInsetsCompat.Type.navigationBars() 而非 Type.systemBars() 或直接 getSystemWindowInsetBottom()—— 后者会包含键盘 Insets。
  3. 低版本(API < 21)不生效?

    • Android 5.0(API 21)以下无官方 Insets API,若需兼容,可通过 反射获取系统资源 间接判断(但精度较低,建议最低兼容到 API 21):
      // 兼容API < 21:通过系统资源判断导航栏是否存在(无法实时判断显示/隐藏)
      public static boolean hasNavigationBar(Context context) {Resources res = context.getResources();int resourceId = res.getIdentifier("config_showNavigationBar", "bool", "android");if (resourceId > 0) {return res.getBoolean(resourceId);}return false; // 无法判断时默认返回false
      }
      

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/96657.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/96657.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

你知道服务器和电脑主机的区别吗?

我们都知道服务器和台式主机有着不同之处&#xff0c;但具体说出个一二三来很多人还是一头雾水&#xff0c;也就是知其然不知其所以然&#xff0c;都是CPU主板 内存 硬盘 电源&#xff0c;撑死就差一个显卡不同&#xff0c;但其实服务器和我们正常使用的台式主机差距很大&#…

什么是包装类

什么是包装类 在Java中&#xff0c;包装类&#xff08;Wrapper Class&#xff09;是为基本数据类型提供的对应的引用类型。Java中的基本数据类型&#xff08;如int、char、boolean等&#xff09;不是对象&#xff0c;为了在需要对象的场景中使用基本数据类型&#xff08;如集合…

用Python打造专业级老照片修复工具:让时光倒流的数字魔法

在这个数字化时代&#xff0c;我们手中珍藏着许多泛黄、模糊、甚至有划痕的老照片。这些照片承载着珍贵的回忆&#xff0c;但时间的侵蚀让它们失去了往日的光彩。今天&#xff0c;我将带您一起用Python开发一个专业级的老照片修复工具&#xff0c;让这些珍贵的记忆重现光彩。为…

linux中查找包含xxx内容的文件

linux中怎么查找哪个文件包含xxx内容 在Linux中查找包含特定内容的文件 在Linux系统中&#xff0c;有几种常用方法来查找包含特定内容的文件。以下是几种最有效的方法&#xff1a;1. 使用 grep 命令&#xff08;最常用&#xff09; 基本语法&#xff1a;bash grep -r "搜索…

sklearn 加州房价数据集 fetch_california_housing 出错 403: Forbidden 修复方案

问题 加载加州房价数据时出现 403 错误 HTTP Error 403: Forbidden from sklearn.datasets import fetch_california_housingcalifornia fetch_california_housing() print(california.target.shape) 解决方案 运行下述代码&#xff0c;然后再运行上述的 fetch_california_hou…

嵌入式学习---(硬件)

1、在LED实验中&#xff0c;在对Soc引脚配置时都做了哪些工作&#xff1f;复用功能配置操作寄存器&#xff1a;IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03将引脚的低 4 位设置为 0101&#xff0c;将引脚复用为 GPIO 功能电气特性配置操作寄存器&#xff1a;IOMUXC_SW_PAD_CTL_PAD_GPIO1…

微信小程序开发教程(十一)

目录&#xff1a;1.上拉触底案例-初步实现上拉触底效果2.上拉触底案例-添加loading效果3.上拉触底案例-节流处理4.扩展-自定义编译模式1.上拉触底案例-初步实现上拉触底效果页面加载的时候调用这个方法&#xff1a;设置样式&#xff1a;下拉触底后继续调用获取颜色的方法2.上拉…

Android相机API2,基于GLSurfaceView+SurfaceTexture实现相机预览,集成的相机算法采用GPU方案,简要说明

Android相机API2&#xff0c;基于GLSurfaceViewSurfaceTexture实现相机预览&#xff0c;集成的相机算法采用GPU方案&#xff0c;简要流程如下(不叠加相机算法的预览显示流程也大体如此&#xff0c;只是去掉了算法部分)&#xff1a;进入相机&#xff1a;1&#xff0c;新建实现了…

[code-review] 日志机制 | `LOG_LEVEL`

第6章&#xff1a;日志机制&#xff08;调试&#xff09; 欢迎来到我们了解ChatGPT-CodeReview项目的最后一章 在第5章&#xff1a;文件过滤逻辑&#xff08;范围管理器&#xff09;中&#xff0c;我们学习了机器人如何智能地决定哪些文件需要发送给AI审查。 但一旦机器人开…

n8n工作流平台入门学习指南

目录 1、基础背景 2、核心概念 2.1 节点(Nodes) 2.2 连接(Connections) 2.3 工作流(Workflows) 3、常用节点说明 4、基于Docker快速部署 5、学习资料 6、常见问题 强烈推荐&#xff0c;大家不懂的直接问&#xff1a;N8N大师&#xff08;GPT&#xff09;&#xff0c;科…

【Oracle经验分享】字符串拼接过长问题的解决方案 —— 巧用 XMLAGG

&#x1f4d1; 目录&#x1f50d; 问题背景⚠️ 常见拼接方式的限制&#x1f4a1; XMLAGG 的解决方案&#x1f4dd; 示例代码&#x1f4cc; 注意事项✅ 总结&#x1f50d; 问题背景在日常开发中&#xff0c;我们经常需要把多行数据拼接成一个字符串。例如将某个字段的多条记录拼…

AJAX入门-URL、参数查询、案例查询

本系列可作为前端学习系列的笔记&#xff0c;代码的运行环境是在VS code中&#xff0c;小编会将代码复制下来&#xff0c;大家复制下来就可以练习了&#xff0c;方便大家学习。 HTML、CSS、JavaScript系列文章 已经收录在前端专栏&#xff0c;有需要的宝宝们可以点击前端专栏查…

【SpringBoot】24 核心功能 - Web开发原理 -Spring Boot 异常处理机制

前言 在开发 Web 应用程序时&#xff0c;异常处理是一个至关重要的部分。Spring Boot 提供了一套强大的异常处理机制&#xff0c;使得开发者能够轻松地处理和响应各种异常情况。本文将深入探讨 Spring Boot 中的异常处理机制&#xff0c;包括默认的错误处理规则、定制错误处理逻…

JVM第一部分

PC寄存器&#xff1a;存储的是数字 0, 3, 6, 10, 17 这样的字节码偏移量。 LineNumberTable&#xff1a;是一个映射表&#xff0c;它将上述的偏移量“翻译”成我们程序员能看懂的源代码行号。 JVM堆 JVM堆由两部分组成&#xff1a;年轻代老年代 年轻代包括三部分&#xff1a;ed…

IDEA使用Maven和MyBatis简化数据库连接(配置篇)

目录&#xff1a; Maven:简化项目构建 MyBatis:简化Jdbc Maven&#xff1a;是一款项目构建与依赖管理工具&#xff0c;核心作用是自动化项目编译、打包等流程&#xff0c;并统一管理项目所需的第三方 Jar 包&#xff08;如 MyBatis 的 Jar 包&#xff09;。 MyBatis&#xf…

Java 泛型详解:从基础到高级应用

目录 一、泛型的基本概念 为什么需要泛型&#xff1f; 二、泛型类与泛型接口 【1】定义泛型类 【2】定义泛型接口 三、泛型方法 四、泛型通配符 【1】无界通配符&#xff08;?&#xff09; 【2】上界通配符&#xff08;? extends T&#xff09; 【3】下界通配符&am…

嵌入式 Linux 启动机制全解析:从 Boot 到 Rootfs

&#x1f680; 嵌入式 Linux 启动机制全解析&#xff1a;从 Boot 到 Rootfs 在嵌入式系统中&#xff0c;Linux 的启动流程不仅是内核加载的过程&#xff0c;更是 bootloader、设备树、初始根文件系统、启动配置文件等多个组件协同工作的结果。不同的文件系统和启动方式会影响系…

Python 操作Office的PPT、Word、Excel,同时兼容WPS

文章目录概要一、环境准备1. 安装必要的Python库2. 系统要求二、核心实现原理1. 检测已安装的Office类型2. 初始化对应的应用程序三、完整代码实现四、使用示例五、WPS兼容处理详解1. 形状和文本框访问兼容处理2. PPT图片粘贴兼容处理3. 资源释放的重要性六、图片操作实现详解1…

ISP之DHCPv6-PD(前缀代理)为用户下发前缀

一、组网需求家庭用户要使用IPv6地址接入互联网。为方便用户接入&#xff0c;运营商使用DHCPv6-PD的方式给家用路由器下发IPv6地址前缀&#xff0c;用户路由器LAN侧不需要手工指定链路的IPv6地址前缀&#xff0c;家用路由器可以给用户终端自动配置IPv6地址和其它网络参数。本例…

Django全栈班v1.04 Python基础语法 20250912 上午

rm 删除命令 注意&#xff1a;删除操作是不可逆的&#xff0c;一旦删除就无法撤销&#xff0c;请谨慎使用。删除文件&#xff1a; rm file.py递归删除目录&#xff1a; rm -r demo/强制删除&#xff1a; rm -f file.py交互式删除&#xff1a; rm -i *.txt课程定位 “学习Python…