虚拟键盘工具类

1,332 阅读2分钟

虚拟键盘,即导航栏,如位置见下图。

图片网络转载 *图片网络转载*

fitsSystemWindows的使用

  • 通过布局设置fitsSystemWindows,使内容区预留

    • fitsSystemWindows 生效前提:当前页面没有标题栏,并且状态栏或者底部导航栏透明
    • fitsSystemWindows = true,表示内容区不延伸到状态栏或底部导航栏
    • fitsSystemWindows = false,表示内容区延伸到状态栏或底部导航栏
    • 详情见链接

设置虚拟键盘颜色

window.clearFlags(
   WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS
           or WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION
)
window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
       or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
       or View.SYSTEM_UI_FLAG_LAYOUT_STABLE)
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
window.navigationBarColor = Color.BLACK

隐藏状态栏

val uiOptions =
    View.SYSTEM_UI_FLAG_FULLSCREEN or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_IMMERSIVE;
window.decorView.systemUiVisibility = uiOptions;

//适配刘海屏
if (android.os.Build.VERSION.SDK_INT >= 28) {
    val lp = window.attributes
    lp.layoutInDisplayCutoutMode =
        WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
    window.attributes = lp
}

拓展工具

WindowInsetsControllerCompat最新 API推荐使用

  • 开启导航栏
ViewCompat.getWindowInsetsController(findViewById<FrameLayout>(android.R.id.content))?.let { controller ->
         controller.show(WindowInsetsCompat.Type.navigationBars())
         controller.isAppearanceLightNavigationBars = true//true icon黑色,false白色
   }
  • 关闭导航栏
ViewCompat.getWindowInsetsController(findViewById<FrameLayout>(android.R.id.content))?.let { controller ->
         controller.hide(WindowInsetsCompat.Type.navigationBars())
         controller.systemBarsBehavior =
            WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
   }
  • 是否显示导航栏

   val FragmentActivity.windowInsetsCompat: WindowInsetsCompat?
    get() = ViewCompat.getRootWindowInsets(findViewById<FrameLayout>(android.R.id.content))

fun FragmentActivity.hasNavigationBars(): Boolean {
    val windowInsetsCompat = windowInsetsCompat ?: return false
    return windowInsetsCompat.isVisible(Type.navigationBars())
            && windowInsetsCompat.getInsets(Type.navigationBars()).bottom > 0
}
  • 获取导航栏高度
val FragmentActivity.windowInsetsCompat: WindowInsetsCompat?
    get() = ViewCompat.getRootWindowInsets(findViewById<FrameLayout>(android.R.id.content))
    
fun FragmentActivity.getNavigationBarsHeight(): Int {
    val windowInsetsCompat = windowInsetsCompat ?: return 0
    return windowInsetsCompat.getInsets(Type.navigationBars()).bottom
}

异常情况

  • 弹出 Dialog,导致Activity失去焦点,导航栏会再次出现
/**
 * @author Vincent
 * @date 2022/8/4
 * @description
 *
 */
class LoadingDialog(context: Context):Dialog(context, R.style.WaitDialogStyle) {

    override fun create() {
        super.create()
        setContentView(R.layout.dialog_loading)
    }

    override fun show() {
        window?.setFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE)
        super.show()
        val uiOptions = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
                View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or
                View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or
                View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY or
                View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
                View.SYSTEM_UI_FLAG_FULLSCREEN
        window?.decorView?.systemUiVisibility = uiOptions
        window?.clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE)
    }
}

补充

状态栏隐藏,提供了三种行为模式,分别是如下:

  • WindowInsetsControllerCompat.BEHAVIOR_SHOW_BARS_BY_TOUCH

滑动内容区域会显示导航栏,导航栏背景默认黑色

  • WindowInsetsControllerCompat.BEHAVIOR_SHOW_BARS_BY_SWIPE

刷新导航栏区域,会出现导航栏,导航栏背景默认黑色

  • WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE

从屏幕边缘滑动,会出现导航栏,导航栏背景有一定的透明度,类似半透明状态,且显示短暂一会会自动消失

如果上面的自动隐藏不符合需求,需要在滑动后规定时间才隐藏导航栏的话,那么有什么方法可以监听导航栏呢?我们可以尝试监听页面系统状态可见性更改,比如导航栏隐藏会显示。然后在显示的情况下,开启定时任务,即可在规定时间隐藏导航栏,代码如下:

override fun onCreate(savedInstanceState: Bundle?) {
    ...
    window.decorView.setOnApplyWindowInsetsListener { v, insets ->
        hideNavigationBars()
        insets
    }
    ...
}

/**
 * 关闭导航栏
 */
open fun hideNavigationBars(){
    if(ViewCompat.getRootWindowInsets(findViewById<FrameLayout>(android.R.id.content))?.isVisible(WindowInsetsCompat.Type.navigationBars()) == true){
        mBinding.root.postDelayed({
            WindowCompat.getInsetsController(window,findViewById<FrameLayout>(android.R.id.content)).let { controller ->
                controller.hide(WindowInsetsCompat.Type.navigationBars())
                controller.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_BARS_BY_SWIPE
            }
        },3000)

    }
}