WindowInsetsControllerCompat使用,新方式实现状态栏、导航栏、键盘控制

WindowInsetsControllerCompat使用

添加依赖

core-ktx1.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) 这个方法是直接沉浸入导航栏和状态栏,
所以新增一个方法用来设置:

  1. 沉浸入导航栏和状态栏,
  2. 只沉浸入状态栏
  3. 只沉浸入导航栏
/**
 * 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. 存在的问题

只在dialogfragment好像好像调用上面的打开键盘的方法没有效果,也有可能是我用错了。

所以当需要配合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日

  1. 为了兼容低版本获取到WindowInsetsControllerCompat
    需要:
    ViewCompat.getWindowInsetsController(window.decorView) 变更成 ViewCompat.getWindowInsetsController(findViewById<FrameLayout>(android.R.id.content))
  2. 新增获取状态栏的高度和导航栏高度,
  3. 判断是否有导航栏

更新-2022年10月19日

  1. 新增一个方法来控制沉浸:
    1. 沉浸入导航栏和状态栏,Type.systemBars()
    2. 只沉浸入状态栏:Type.statusBars()
    3. 只沉浸入导航栏: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
) 
  1. 注意事项:获取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,我也可能没有用到,建议多看看官方文档。

评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

TieJun~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值