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

本文详细介绍了如何使用WindowInsetsControllerCompat进行Android应用的状态栏、导航栏的沉浸式设置,包括全屏切换、显示隐藏状态栏和导航栏的方法,以及键盘显示、隐藏和监听键盘高度变化的实现。此外,还提供了兼容不同Android版本的解决方案。
摘要由CSDN通过智能技术生成

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
}

二、键盘操作
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日

  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
) 

注意事项:获取windowInsets时,在oncreate等一些类似场景中直接调用,需要post 一下,否则会返回null
eg:
 

xxView.post{
  ViewCompat.getRootWindowInsets(findViewById(android.R.id.content))
}


————————————————
版权声明:本文为CSDN博主「shetj」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/StjunF/article/details/121840122

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值