WindowInsetsControllerCompat使用
文章目录
添加依赖
从core-ktx
的1.5.0
版本开始就是可以使用这个了。
首先添加依赖
implementation "androidx.core:core-ktx:1.7.0"
大概有以下类型
WindowInsetsCompat.Type.statusBars()
WindowInsetsCompat.Type.navigationBars()
WindowInsetsCompat.Type.captionBar()
WindowInsetsCompat.Type.ime()
WindowInsetsCompat.Type.systemGestures()
WindowInsetsCompat.Type.mandatorySystemGestures()
WindowInsetsCompat.Type.tappableElement()
WindowInsetsCompat.Type.displayCutout()
WindowInsetsCompat.Type.systemBars()
这里讲到的是:
WindowInsetsCompat.Type.ime() //键盘
WindowInsetsCompat.Type.statusBars() //状态栏
WindowInsetsCompat.Type.navigationBars() //导航栏
WindowInsetsCompat.Type.systemBars() //状态栏、导航栏和标题栏
一、状态栏、导航栏相关
1. 沉浸式设置
WindowCompat.setDecorFitsSystemWindows(window, false)
第二参数decorFitsSystemWindows
表示是否沉浸,false 表示沉浸,true表示不沉浸
示例,沉浸入状态栏,状态栏字体黑色、底色透明:
//关键代码,沉浸
WindowCompat.setDecorFitsSystemWindows(window, false)
//设置专栏栏和导航栏的底色,透明
window.statusBarColor = Color.TRANSPARENT
window.navigationBarColor = Color.TRANSPARENT
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
window.navigationBarDividerColor = Color.TRANSPARENT
}
//设置沉浸后专栏栏和导航字体的颜色,
ViewCompat.getWindowInsetsController(findViewById<FrameLayout>(android.R.id.content))?.let { controller ->
controller.isAppearanceLightStatusBars = isBlack
controller.isAppearanceLightNavigationBars = isBlack
}
update 2022年10月19日:
因为WindowCompat.setDecorFitsSystemWindows(window, false)
这个方法是直接沉浸入导航栏和状态栏,
所以新增一个方法用来设置:
- 沉浸入导航栏和状态栏,
- 只沉浸入状态栏
- 只沉浸入导航栏
/**
* Immerse 沉浸。设置沉浸的方式
*
* @param type Type.systemBars(),Type.statusBars(),Type.navigationBars()
* @param statusIsBlack 专栏文字 true 黑色,false 白色
* @param navigationIsBlack 导航栏按钮 true 黑色,false 白色
* @param color 状态和导航栏的背景颜色
*/
@JvmOverloads
fun Activity.immerse(
@Type.InsetsType type: Int = Type.systemBars(),
statusIsBlack: Boolean = true,
navigationIsBlack: Boolean = true,
@ColorInt color: Int = Color.TRANSPARENT
) {
when (type) {
Type.systemBars() -> { //沉浸入导航栏和状态栏
WindowCompat.setDecorFitsSystemWindows(window, false)
updateSystemUIColor(color)
windowInsetsController.let { controller ->
controller.isAppearanceLightStatusBars = statusIsBlack
controller.isAppearanceLightNavigationBars = navigationIsBlack
}
findViewById<FrameLayout>(android.R.id.content).apply {
setPadding(0, 0, 0, 0)
}
}
Type.statusBars() -> {//只沉浸入状态栏
WindowCompat.setDecorFitsSystemWindows(window, false)
window.statusBarColor = color
windowInsetsController.isAppearanceLightStatusBars = statusIsBlack
findViewById<FrameLayout>(android.R.id.content).apply {
post {
setPadding(0, 0, 0, getNavigationBarsHeight())
}
}
}
Type.navigationBars() -> {//只沉浸入导航栏
WindowCompat.setDecorFitsSystemWindows(window, false)
window.navigationBarColor = color
windowInsetsController.isAppearanceLightNavigationBars = navigationIsBlack
findViewById<FrameLayout>(android.R.id.content).apply {
post {
setPadding(0, getStatusBarsHeight(), 0, 0)
}
}
}
else -> {
// no work
}
}
}
2. 全屏
WindowInsetsCompat.Type.systemBars()
表示状态栏、导航栏和标题栏
fun Activity.hideSystemUI() {
WindowCompat.setDecorFitsSystemWindows(window, false)
ViewCompat.getWindowInsetsController(window.decorView)?.let { controller ->
controller.hide(WindowInsetsCompat.Type.systemBars())
controller.systemBarsBehavior =
WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
}
}
3. 退出全屏
fun Activity.showSystemUI() {
WindowCompat.setDecorFitsSystemWindows(window, true)
ViewCompat.getWindowInsetsController(findViewById<FrameLayout>(android.R.id.content))?.show(WindowInsetsCompat.Type.systemBars())
}
4. 其他
状态栏和标题栏都是可以单独控制的。
1. 显示状态栏,字体黑色
window.statusBarColor = Color.TRANSPARENT //设置底色
ViewCompat.getWindowInsetsController(findViewById<FrameLayout>(android.R.id.content))?.let { controller ->
controller.show(WindowInsetsCompat.Type.statusBars())
controller.isAppearanceLightStatusBars = true//true字体黑色,false白色
}
2. 关闭状态栏
ViewCompat.getWindowInsetsController(activity.findViewById<FrameLayout>(android.R.id.content))?.hide(WindowInsetsCompat.Type.statusBars())
3. 显示导航栏
ViewCompat.getWindowInsetsController(findViewById<FrameLayout>(android.R.id.content))?.let { controller ->
controller.show(WindowInsetsCompat.Type.navigationBars())
controller.isAppearanceLightNavigationBars = true//true icon黑色,false白色
}
4. 隐藏导航栏
fun FragmentActivity.hideNavigationBars() {
ViewCompat.getWindowInsetsController(findViewById<FrameLayout>(android.R.id.content))?.let { controller ->
controller.hide(Type.navigationBars())
controller.systemBarsBehavior =
WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
}
}
5. 判断是否显示导航栏
val FragmentActivity.windowInsetsCompat: WindowInsetsCompat?
get() = ViewCompat.getRootWindowInsets(findViewById(android.R.id.content))
fun FragmentActivity.hasNavigationBars(): Boolean {
val windowInsetsCompat = windowInsetsCompat ?: return false
return windowInsetsCompat.isVisible(Type.navigationBars())
&& windowInsetsCompat.getInsets(Type.navigationBars()).bottom > 0
}
6. 获取导航栏的高度
val FragmentActivity.windowInsetsCompat: WindowInsetsCompat?
get() = ViewCompat.getRootWindowInsets(findViewById(android.R.id.content))
fun FragmentActivity.getNavigationBarsHeight(): Int {
val windowInsetsCompat = windowInsetsCompat ?: return 0
return windowInsetsCompat.getInsets(Type.navigationBars()).bottom
}
7. 获取状态栏的高度
val FragmentActivity.windowInsetsCompat: WindowInsetsCompat?
get() = ViewCompat.getRootWindowInsets(findViewById(android.R.id.content))
fun FragmentActivity.getStatusBarsHeight(): Int {
val windowInsetsCompat = windowInsetsCompat ?: return 0
return windowInsetsCompat.getInsets(Type.statusBars()).top
}
8. 沉浸相关:ComponentActivity.enableEdgeToEdge()
App沉浸状态栏和导航栏设置官方 ComponentActivity 提供了 enableEdgeToEdge(),可以直接使用,就不需要自己处理下列的版本兼容问题。
可以自行看下源码,后使用
fun ComponentActivity.enableEdgeToEdge(
statusBarStyle: SystemBarStyle = SystemBarStyle.auto(Color.TRANSPARENT, Color.TRANSPARENT),
navigationBarStyle: SystemBarStyle = SystemBarStyle.auto(DefaultLightScrim, DefaultDarkScrim)
)
二、键盘操作
1. 显示和隐藏键盘
@JvmStatic
fun hideSoftKeyboard(activity: Activity) {
ViewCompat.getWindowInsetsController(activity.findViewById<FrameLayout>(android.R.id.content))
?.hide(WindowInsetsCompat.Type.ime())
}
@JvmStatic
fun showSoftKeyboard(activity: Activity) {
ViewCompat.getWindowInsetsController(activity.findViewById<FrameLayout>(android.R.id.content))
?.show(WindowInsetsCompat.Type.ime())
}
2. 存在的问题
只在dialog
、fragment
好像好像调用上面的打开键盘的方法没有效果,也有可能是我用错了。
所以当需要配合EditText
打开的时候还是用老的方法
@JvmStatic
fun focusEditShowKeyBoard(editText: EditText) {
editText.isEnabled = true
editText.isFocusable = true
editText.isFocusableInTouchMode = true
editText.requestFocus()
val inputManager = editText.context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
editText.setSelection(editText.text.length)
inputManager.showSoftInput(editText, 0)
}
3. 监听键盘高度变化
fun addKeyBordHeightChangeCallBack(view: View, onAction:(height:Int) ->Unit){
var posBottom: Int
if (VERSION.SDK_INT >= VERSION_CODES.R) {
val cb = object : WindowInsetsAnimation.Callback(DISPATCH_MODE_STOP) {
override fun onProgress(
insets: WindowInsets,
animations: MutableList<WindowInsetsAnimation>
): WindowInsets {
posBottom = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom +
insets.getInsets(WindowInsetsCompat.Type.systemBars()).bottom
onAction.invoke(posBottom)
return insets
}
}
view.setWindowInsetsAnimationCallback(cb)
} else {
ViewCompat.setOnApplyWindowInsetsListener(view) { _, insets ->
posBottom = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom +
insets.getInsets(WindowInsetsCompat.Type.systemBars()).bottom
onAction.invoke(posBottom)
insets
}
}
}
使用:
var posBottom = 0
var maxBottom = 0
addKeyBordHeightChangeCallBack(mViewBinding.root) {
posBottom = it
maxBottom = max(maxBottom,posBottom)
mViewBinding.guideline2.updateLayoutParams<ConstraintLayout.LayoutParams> {
guidePercent = 1 - posBottom.toFloat() / screenHeight
}
mViewBinding.guideline.updateLayoutParams<ConstraintLayout.LayoutParams> {
guidePercent =lastPercent - ((lastPercent-0.5f) * (posBottom.toFloat() / maxBottom)
}
}
更新-2022年4月1日
- 为了兼容低版本获取到
WindowInsetsControllerCompat
需要:
ViewCompat.getWindowInsetsController(window.decorView)
变更成ViewCompat.getWindowInsetsController(findViewById<FrameLayout>(android.R.id.content))
- 新增获取状态栏的高度和导航栏高度,
- 判断是否有导航栏
更新-2022年10月19日
- 新增一个方法来控制沉浸:
- 沉浸入导航栏和状态栏,Type.systemBars()
- 只沉浸入状态栏:Type.statusBars()
- 只沉浸入导航栏:Type.navigationBars()
fun Activity.immerse(
@Type.InsetsType type: Int = Type.systemBars(), //Type.systemBars(),Type.statusBars(),Type.navigationBars()
statusIsBlack: Boolean = true,
navigationIsBlack: Boolean = true,
@ColorInt color: Int = Color.TRANSPARENT
)
- 注意事项:获取windowInsets时,在
oncreate
等一些类似场景中直接调用,需要post 一下,否则会返回null
eg:
xxView.post{
ViewCompat.getRootWindowInsets(findViewById(android.R.id.content))
}
或者
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
insets
}
更新-2024年4月1日
App沉浸状态栏和导航栏设置,好像去年官方就已经给 ComponentActivity 提供了 enableEdgeToEdge(),可以直接使用,就不需要自己处理下列的版本兼容问题。
fun ComponentActivity.enableEdgeToEdge(
statusBarStyle: SystemBarStyle = SystemBarStyle.auto(Color.TRANSPARENT, Color.TRANSPARENT),
navigationBarStyle: SystemBarStyle = SystemBarStyle.auto(DefaultLightScrim, DefaultDarkScrim)
)
文章内容不一定的最新~有些API,我也可能没有用到,建议多看看官方文档。