项目场景:
最近在项目开发中,遇到一个兼容性的问题。在Fragment 中 从底部弹出一个 继承 DialogFragment 的自定义Dialog ,布局里面有 EditText , 弹出 dialog 后,EditText 获取焦点,弹出键盘后,dialog 没有被顶上去,而是完完全全的被键盘遮挡住了。
在特定机型:ViVo Y9/Y50 上面复现。
问题描述
一开始的代码如下:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
dialog?.window?.apply {
setWindowAnimations(R.style.base_dialog_bottom_anim)
setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE or WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN)
}
// 省略view 实例化代码
// 延迟300 ms主动弹出键盘
Handler(Looper.getMainLooper()).postDelayed({
SoftKeyboardUtil.openKeyBoard(binding.editName)
}, 300)
}
对于键盘遮挡布局的问题,网上大部分的方案都是设置 setSoftInputMode
无论是在 Manifest 中给 Activity 设置 android:windowSoftInputMode="adjustResize|adjustPan"
还是通过代码设置:getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE | WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN)
对于这些方案,笔者都尝试过,对于大部分的手机都是有效的。但是仅仅对于ViVo 部分手机无效。
兼容性的问题很是让人蛋疼…
原因分析:
笔者尝试过好几种方案,当键盘弹出来的时候,手动改变 弹出的 dialog 的显示位置。
1、比对 键盘弹出前 和弹出后, dialog?.window?.layoutParams
中的 坐标 y 的位置;结果:失败。因为无论是正常手机还是有兼容性的手机,无论是dialog 被 键盘顶上去 还是被遮挡, y 的值始终是0.
2、…
解决方案:
最后的解决办法是,因为手机屏幕是固定的,宽高也是固定的。而 dialog 布局显示的位置 是不固定的。
画一个粗糙的图展示下:
当有兼容性的手机无论是弹出键盘还是不弹出键盘,其 在屏幕中 的坐标 y 始终是 0 ,而对于正常的 手机 不弹出键盘 其坐标 y 为0 ,弹出键盘 其在屏幕中的坐标则是 location[1] 。
所以我们可以通过 dialog 的布局在屏幕中的位置也判断 当前dialog 是否被键盘遮挡还是正常顶起来。然后通过动态的修改 dialog 的 LayoutParam 的 坐标 y 来改变dialog 的位置,从而能让dialog 不至于被键盘遮挡。
修改后的代码如下:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
dialog?.window?.apply {
setWindowAnimations(R.style.base_dialog_bottom_anim)
setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE or WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN)
}
// 获取整个 window 的高度
requireActivity().window?.apply {
windowHeight = decorView.rootView.height
}
// 设置键盘 弹起与关闭 的监听
globalListener =
SoftKeyboardUtil.setOnKeyboardListener(requireActivity(), object : SoftKeyboardUtil.OnKeyboardStateChangedListener {
override fun onKeyBoardShow(boardHeight: Int) {
val intArray = IntArray(2)
// 获取dialog 的根布局在 屏幕中的位置
binding.root.getLocationOnScreen(intArray)
// 如果是正常手机,则 intArray[1] + contentHeight 的和一定小于 屏幕的高度,没有被键盘弹起的一定是大于等于。看图...
if (intArray[1] + contentHeight >= windowHeight) {
// 如果 dialog 被遮挡了,手动把dialog 移动到键盘上面位置
dialog?.window?.updateLayoutParams {
y = boardHeight
}
}
}
override fun onKeyBoardHide() {
// 键盘关闭,则 把 dialog 移动到底部。
dialog?.window?.updateLayoutParams {
y = 0
x = 0
}
}
})
Handler(Looper.getMainLooper()).postDelayed({
// 获取 dialog 布局的高度。
contentHeight= binding.root.measuredHeight
SoftKeyboardUtil.openKeyBoard(binding.editName)
}, 300)
}