【大道至简】官方兼容到android13+的获取系统屏幕高度, statusbar,navBar

android在屏幕高度和app高度,statusbar, navigationbar的高度处理上,迭代了好多版本。

android11, android12都有新的api和过时的api标记。

涉及的api类似如下:

windowManager,defaultDisplay, Context.display, DecorView, windowInsets, Compat兼容库, getRealSize, getSize,

currentWindowMetrics, maximumWindowMetrics, 通过resources获取资源id navigation_bar_height等等等等。

网上的帖子跟api一样,实现的五花八门太多了。

花了2天时间,终于找到了官方,而且最合适的方案:

全网似乎都没有这么简洁和准确的方案了:
implemention "androidx.window:window:1.2.0" 引入后,写上下面2个函数:

kotlin 复制代码
//随意调用,官方出品最精简,最准确。
fun Activity.getScreenFullSize() : Pair<Int, Int> {
    val m = WindowMetricsCalculator.getOrCreate().computeCurrentWindowMetrics(this)
    //computeMaximumWindowMetrics(this) 区别就是多屏,类似华为推上去的效果。不分屏就是一样的。
    return m.bounds.width() to m.bounds.height()
}

/**
* 获取当前的statusBar的高度和navigationBar高度。如果在onCreate,onStart, onResume调用,必须经过一轮View.post之后。
* 如果你明确知道activity已经ok则直接调用。
*/
fun Activity.currentStatusBarAndNavBarHeight() : Pair<Int, Int>? {
    val insets = ViewCompat.getRootWindowInsets(window.decorView) ?: return null
    val nav = insets.getInsets(WindowInsetsCompat.Type.navigationBars()).bottom
    val sta = insets.getInsets(WindowInsetsCompat.Type.statusBars()).top
    return sta to nav
}

接下来介绍一下:

想要屏幕高宽

getScreenFullSize() 即 WindowMetricsCalculator.getOrCreate().computeCurrentWindowMetrics(activity)

无需等待界面渲染成功,即在onCreate就可以调用,而且里面已经做了低版本兼容,感谢jetpack window库

获取的就是整个屏幕的高度。包含了statusBar,navigationBar的高度一起。与adb shell wm size一致。

这个方法100%可靠。虽然我们看api上描述说低版本(由于navigationBar可能获取不到)近似值,但也是最接近最合理的值,不会是0的。所以这个函数就是目前集大成者。

点击computeCurrentWindowMetrics进入查看源码:

kt 复制代码
 val bounds = if (Build.VERSION.SDK_INT >= VERSION_CODES.R) {
      currentWindowBounds(activity) //往下就是wm.currentWindowMetrics.bounds
  } else if (Build.VERSION.SDK_INT >= VERSION_CODES.Q) {
      computeWindowBoundsQ(activity) //反射Configuration windowConfiguration bounds
  } else if (Build.VERSION.SDK_INT >= VERSION_CODES.P) {
      computeWindowBoundsP(activity) //反射Configuration windowConfiguration bounds
  } else if (Build.VERSION.SDK_INT >= VERSION_CODES.N) {
      computeWindowBoundsN(activity) //display realSize + navigationHeight Id获取
  } else {
      computeWindowBoundsIceCreamSandwich(activity)
  }

由此可见,这个就是集大成者。
大家放心使用getScreenFullSize,WindowMetricsCalculator.getOrCreate().computeCurrentWindowMetrics(this),来获取屏幕的高宽。官方出品最精简,最准确。

想要SystembarHeight和navBarHeight

大家一般都见过这个代码:

kt 复制代码
fun Window.transparentStatusBar(
    isBlackStatusBarTextColor: Boolean? = null,
    isBlackNavigationBarTextColor: Boolean? = null,
    crossinline insetsBlock: (
        insets: WindowInsetsCompat,
        statusBarsHeight: Int,
        navigationBarHeight: Int
    ) -> WindowInsetsCompat = {insets, _, _ -> insets}
) {
    ViewCompat.setOnApplyWindowInsetsListener(decorView) { _, insets ->
        val bottom = insets.getInsets(WindowInsetsCompat.Type.navigationBars()).bottom
        val top = insets.getInsets(WindowInsetsCompat.Type.statusBars()).top
        insetsBlock.invoke(
            insets,
            top,
            bottom
        )
    }
    WindowCompat.setDecorFitsSystemWindows(this, false)
    statusBarColor = Color.TRANSPARENT

    if (isBlackStatusBarTextColor != null || isBlackNavigationBarTextColor != null) {
        WindowCompat.getInsetsController(this, decorView).apply {
            if (isBlackStatusBarTextColor != null) {
                isAppearanceLightStatusBars = isBlackStatusBarTextColor
            }
            if (isBlackNavigationBarTextColor != null) {
                isAppearanceLightNavigationBars = isBlackNavigationBarTextColor
            }
        }
    }
}

我们经过用上述代码来做沉浸式。就是程序在statusBar之下,也可以在navigationBar之下。更加延展。

这里其实有2个注意点:

这个函数如果不调用
WindowCompat.setDecorFitsSystemWindows(this, false) 对于constraintLayout是不会回调的。他默认fitsystemWindow。

我们如果仅仅ViewCompat.setOnApplyWindowInsetsListener是不一定有回调的。

所以使用注意事项:

  1. 你如果做了沉浸式,即调用了WindowCompat.setDecorFitsSystemWindows(this, false) + setOnApplyWindowInsetsListener,则可以在回调监听获取
kt 复制代码
        val bottom = insets.getInsets(WindowInsetsCompat.Type.navigationBars()).bottom //navBarHeight
        val top = insets.getInsets(WindowInsetsCompat.Type.statusBars()).top //statusbarHeight
  1. 你如果没有做沉浸式或者setOnApplyWindowInsetsListener不生效,那么调用currentStatusBarAndNavBarHeight().

    即,ViewCompat.getRootWindowInsets(window.decorView)

    他的说明文档,写了,必须在view Attach到屏幕上,即view.post的执行,就是attach之后运行的。有兴趣可以研究View的post逻辑源码。

    如果明确已经准备好,则无需post了。也就是在onCreate,onResume使用该函数必须post。

  2. 唯一缺点就是,不能动态监听。这也不能说是缺点,他本意就是一次获取。使用场景就是这样。如果你要动态监听,还是类似实现透明一样,通过WindowCompat.setDecorFitsSystemWindows(this, false) + setOnApplyWindowInsetsListener监听实现。

其他备注:

windowManager.currentWindowMetrics和MaximumWindowMetrics的区别:

max永远是屏幕大小。

current就是当前窗口,其实也是包含了statusBar和navigationBar的。但是当多窗口模式,或者分屏,比如华为有这个悬浮应用功能,获取到的current就是变化后的。

相关推荐
程序员陆业聪1 小时前
从 OpenClaw 到 Android:Harness Engineering 是怎么让 Agent 变得可用的
android
hnlgzb3 小时前
常见的Android Jetpack库会有哪些?这些库中又有哪些常用类的?
android·android jetpack
钛态7 小时前
Flutter 三方库 http_mock_adapter — 赋能鸿蒙应用开发的高效率网络接口 Mock 与自动化测试注入引擎(适配鸿蒙 HarmonyOS Next ohos)
android·网络协议·flutter·http·华为·中间件·harmonyos
王码码20357 小时前
Flutter for OpenHarmony:Flutter 三方库 algoliasearch 毫秒级云端搜索体验(云原生搜索引擎)
android·前端·git·flutter·搜索引擎·云原生·harmonyos
左手厨刀右手茼蒿7 小时前
Flutter for OpenHarmony: Flutter 三方库 shamsi_date 助力鸿蒙应用精准适配波斯历法(中东出海必备)
android·flutter·ui·华为·自动化·harmonyos
代码飞天7 小时前
wireshark的高级使用
android·java·wireshark
2501_915918418 小时前
苹果App Store上架审核卡住原因分析与解决方案指南
android·ios·小程序·https·uni-app·iphone·webview
skiy8 小时前
MySQL Workbench菜单汉化为中文
android·数据库·mysql
小小小点9 小时前
Android四大常用布局详解与实战
android
MinQ10 小时前
binder和socket区别及原理
android