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
是不一定有回调的。
所以使用注意事项:
- 你如果做了沉浸式,即调用了
WindowCompat.setDecorFitsSystemWindows(this, false)
+setOnApplyWindowInsetsListener
,则可以在回调监听获取
kt
val bottom = insets.getInsets(WindowInsetsCompat.Type.navigationBars()).bottom //navBarHeight
val top = insets.getInsets(WindowInsetsCompat.Type.statusBars()).top //statusbarHeight
-
你如果没有做沉浸式或者setOnApplyWindowInsetsListener不生效,那么调用currentStatusBarAndNavBarHeight().
即,ViewCompat.getRootWindowInsets(window.decorView)
他的说明文档,写了,必须在view Attach到屏幕上,即view.post的执行,就是attach之后运行的。有兴趣可以研究View的post逻辑源码。
如果明确已经准备好,则无需post了。也就是在onCreate,onResume使用该函数必须post。
-
唯一缺点就是,不能动态监听。这也不能说是缺点,他本意就是一次获取。使用场景就是这样。如果你要动态监听,还是类似实现透明一样,通过
WindowCompat.setDecorFitsSystemWindows(this, false)
+setOnApplyWindowInsetsListener
监听实现。
其他备注:
windowManager.currentWindowMetrics和MaximumWindowMetrics的区别:
max永远是屏幕大小。
current就是当前窗口,其实也是包含了statusBar和navigationBar的。但是当多窗口模式,或者分屏,比如华为有这个悬浮应用功能,获取到的current就是变化后的。