Android(安卓)webView嵌套html页面软键盘遮盖页面问题中级解决方案java kotlin
16lz
2021-01-26
今天遇到一个特别棘手的问题,android 内部原生嵌套webview h5页面时,软键盘被遮挡问题,苦寻半天,找不到是我这边的问题,还是前端的问题,最后这个网址在google浏览器打开,并且打开软键盘,键盘顶起是正常的先上效果图:
原始的位置:
正常不处理时软键盘弹起:
处理后软键盘弹起效果如下
在确定了问题是我的原因之后,开始找答案:
找到了一个大牛写的
AndroidBug5497Workaround
public class AndroidBug5497Workaround { public static void assistActivity(Activity activity) { new AndroidBug5497Workaround(activity); } private View mChildOfContent; private int usableHeightPrevious; private FrameLayout.LayoutParams frameLayoutParams; private int contentHeight; private boolean isfirst = true; private Activity activity; private int statusBarHeight; private AndroidBug5497Workaround(Activity activity) { //获取状态栏的高度 int resourceId = activity.getResources().getIdentifier("status_bar_height", "dimen", "android"); statusBarHeight = activity.getResources().getDimensionPixelSize(resourceId); this.activity = activity; FrameLayout content = (FrameLayout) activity.findViewById(android.R.id.content); mChildOfContent = content.getChildAt(0); //界面出现变动都会调用这个监听事件 mChildOfContent.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { public void onGlobalLayout() { if (isfirst) { contentHeight = mChildOfContent.getHeight();//兼容华为等机型 isfirst = false; } possiblyResizeChildOfContent(); } }); frameLayoutParams = (FrameLayout.LayoutParams) mChildOfContent.getLayoutParams(); } //重新调整跟布局的高度 private void possiblyResizeChildOfContent() { int usableHeightNow = computeUsableHeight(); //当前可见高度和上一次可见高度不一致 布局变动 if (usableHeightNow != usableHeightPrevious) { //int usableHeightSansKeyboard2 = mChildOfContent.getHeight();//兼容华为等机型 int usableHeightSansKeyboard = mChildOfContent.getRootView().getHeight(); int heightDifference = usableHeightSansKeyboard - usableHeightNow; if (heightDifference > (usableHeightSansKeyboard / 4)) { // keyboard probably just became visible if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { //frameLayoutParams.height = usableHeightSansKeyboard - heightDifference; frameLayoutParams.height = usableHeightSansKeyboard - heightDifference + statusBarHeight; } else { frameLayoutParams.height = usableHeightSansKeyboard - heightDifference; } } else { frameLayoutParams.height = contentHeight; } mChildOfContent.requestLayout(); usableHeightPrevious = usableHeightNow; } } /** * 计算mChildOfContent可见高度 ** @return */ private int computeUsableHeight() { Rect r = new Rect(); mChildOfContent.getWindowVisibleDisplayFrame(r); return (r.bottom - r.top); }}
用了之后发现问题,在一些虚拟键盘手机上,虚拟键会遮挡下面的布局
最后在上面进行了改良
kotlin代码
class GlobalLayoutUtils(activity: Activity, private var isImmersed: Boolean = true) { // 当前界面根布局,就是我们设置的 setContentView() private var mChildOfContent: View private var frameLayoutParams: FrameLayout.LayoutParams // 变化前的试图高度 private var usableHeightPrevious = 0 init { val content: FrameLayout = activity.findViewById(android.R.id.content) mChildOfContent = content.getChildAt(0) // 添加布局变化监听 mChildOfContent.viewTreeObserver.addOnGlobalLayoutListener { possiblyResizeChildOfContent(activity) } frameLayoutParams = mChildOfContent.layoutParams as FrameLayout.LayoutParams } private fun possiblyResizeChildOfContent(activity: Activity) { // 当前可视区域的高度 val usableHeightNow = computeUsableHeight() // 当前高度值和之前的进行对比,变化将进行重绘 if (usableHeightNow != usableHeightPrevious) { // 获取当前屏幕高度 // Ps:并不是真正的屏幕高度,是当前app的窗口高度,分屏时的高度为分屏窗口高度 var usableHeightSansKeyboard = mChildOfContent.rootView.height // 高度差值:屏幕高度 - 可视内容高度 var heightDifference = usableHeightSansKeyboard - usableHeightNow // 差值为负,说明获取屏幕高度时出错,宽高状态值反了,重新计算 if (heightDifference < 0) { usableHeightSansKeyboard = mChildOfContent.rootView.width heightDifference = usableHeightSansKeyboard - usableHeightNow } when { // keyboard probably just became visible // 如果差值大于屏幕高度的 1/4,则认为输入软键盘为弹出状态 heightDifference > usableHeightSansKeyboard / 4 -> // 设置布局高度为:屏幕高度 - 高度差 frameLayoutParams.height = usableHeightSansKeyboard - heightDifference // keyboard probably just became hidden // 虚拟导航栏显示 heightDifference >= getNavigationBarHeight(activity) -> frameLayoutParams.height = usableHeightSansKeyboard - getNavigationBarHeight(activity) // 其他情况直接设置为可视高度即可 else -> frameLayoutParams.height = usableHeightNow } } // 刷新布局,会重新测量、绘制 mChildOfContent.requestLayout() // 保存高度信息 usableHeightPrevious = usableHeightNow } /** * 获取可视内容区域的高度 */ private fun computeUsableHeight(): Int { val r = Rect() // 当前窗口可视区域,不包括通知栏、导航栏、输入键盘区域 mChildOfContent.getWindowVisibleDisplayFrame(r) return if (isImmersed) { // 沉浸模式下,底部坐标就是内容有效高度 r.bottom } else { // 非沉浸模式下,去掉通知栏的高度 r.top(可用于通知栏高度的计算) r.bottom - r.top } } // 获取导航栏真实的高度(可能未显示) private fun getNavigationBarHeight(context: Context): Int { var result = 0 val resources = context.resources val resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android") if (resourceId > 0) { result = resources.getDimensionPixelSize(resourceId) } return result }}
java代码
public class GlobalLayoutUtil { private Activity activity; private boolean isImmersed = false; private View mChildOfContent; private FrameLayout.LayoutParams frameLayoutParams; private int usableHeightPrevious = 0; public GlobalLayoutUtil(Activity activity) { this.activity = activity; init(); } public void init() { FrameLayout content = activity.findViewById(android.R.id.content); mChildOfContent = content.getChildAt(0); //添加布局变化监听 mChildOfContent.getViewTreeObserver().addOnGlobalLayoutListener(() -> { possiblyResizeChildOfContent(activity); }); frameLayoutParams = (FrameLayout.LayoutParams) mChildOfContent.getLayoutParams(); } private void possiblyResizeChildOfContent(Activity activity) { //当前可视区域的高度 int usableHeightNow = computeUsableHeight(); //当前高度值和之前的进行对比,变化将进行重新绘制 if (usableHeightNow != usableHeightPrevious) { //获取当前屏幕高度 //Ps: 并不是真正的屏幕高度,是当前app的窗口高度,分屏时的高度为分屏窗口高度 int usableHeightSansKeyboard = mChildOfContent.getRootView().getHeight(); // 高度差值:屏幕高度 - 可视内容高度 int heightDifference = usableHeightSansKeyboard - usableHeightNow; // 差值为负,说明获取屏幕高度时出错,宽高状态值反了,重新计算 if (heightDifference < 0) { usableHeightSansKeyboard = mChildOfContent.getRootView().getWidth(); heightDifference = usableHeightSansKeyboard - usableHeightNow; } // keyboard probably just became visible // 如果差值大于屏幕高度的 1/4,则认为输入软键盘为弹出状态 if (heightDifference > usableHeightSansKeyboard / 4) { // 设置布局高度为:屏幕高度 - 高度差 frameLayoutParams.height = usableHeightSansKeyboard - heightDifference; } else if (heightDifference >= getNavigationBarHeight(activity)) { // keyboard probably just became hidden // 虚拟导航栏显示 frameLayoutParams.height = usableHeightSansKeyboard - getNavigationBarHeight(activity); } else {// 其他情况直接设置为可视高度即可 frameLayoutParams.height = usableHeightNow; } } // 刷新布局,会重新测量、绘制 mChildOfContent.requestLayout(); // 保存高度信息 usableHeightPrevious = usableHeightNow; } /** * 获取可视内容区域的高度 */ private int computeUsableHeight() { Rect rect = new Rect(); mChildOfContent.getWindowVisibleDisplayFrame(rect); if (isImmersed) return rect.bottom; else return rect.bottom - rect.top; } /** * 获取导航栏的真实高度 * * @param context: * @return: 导航栏高度 */ private int getNavigationBarHeight(Context context) { int result = 0; Resources resources = context.getResources(); int resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android"); if (resourceId > 0) result = resources.getDimensionPixelSize(resourceId); return result; }}
特别感谢苦瓜 呱呱 博客地址
更多相关文章
- Android(安卓)dpi、dip、px、分辨率、屏幕尺寸、density 关系以
- 在android设计中,如何在有限的界面上做布局
- Android(安卓)屏幕适配的几种方法
- 粗暴快速Android全屏幕适配方案
- Android(安卓)- 支持不同的设备 - 支持不同的屏幕
- Android(安卓)Touch事件获取手指触摸位置
- android基础知识16:多分辨率屏显设计及其兼容性测试
- Android之机型适配
- 【Rayeager PX2分享】修改安卓开机后进入主屏幕流程分析