解决Android中Webview软键盘与沉浸式的冲突的几种方式的分析
程序员文章站
2022-01-30 20:46:46
...
问题
问题主要出现在使用Webview同时使用沉浸式的时候,软键盘会遮挡webview输入的显示,并且此时webview并不能滑动。这个问题的出自Android本身的一个bug,bug号5497,从编号就能看出这个bug已经很久远了,久远到官方都不出面解决,因为民间已经有了许多方法来绕过,或者部分情况下解决这个问题。
解决的方法有三种:
- 不要使用沉浸式布局
- AndroidBug5497Workaround
- 自定义LinearLayout,relativityLayout
第一种(不要使用沉浸式布局)
故名思议,不要使用沉浸式布局,也就不会发生这个问题了。其实很多情况下,这个都是一条最好的解决方式。
第二种(AndroidBug5497Workaround)
Github上一位国外大神给出的解决方案,对于国产手机疯狂魔改的预计不足,效果能出来,但总是不尽如人意,下面是调整过适配了华为小米部分系统的代码(但其实显示上还是有些问题,比如华为的机型会底部上升一个statusbar的高度,等等),有兴趣并且有信心处理未来诸多机型系统的适配的话,可以一试。
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);
}
}
第三种(自定义LinearLayout,relativityLayout)
此方式适合内部布局比较简单的情况,自己就是用的这种方案,因为自己的布局就是两个Framelayout,一个装载webview,另一个装载遮挡布局。
记得在设置了布局根部使用此控件并且加上android:fitsSystemWindows="true"
代码如下:
- ResizeRelativityLayout:
/**
* ResizeRelativeLayout,配合fitsSystemWindows=true在根布局使用,解决软键盘布局调整的问题
*/
public class ResizeRelativeLayout extends RelativeLayout {
private int[] mInsets = new int[4];
public ResizeRelativeLayout(Context context) {
super(context);
}
public ResizeRelativeLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ResizeRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public ResizeRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
protected final boolean fitSystemWindows(Rect insets) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
// Intentionally do not modify the bottom inset. For some reason,
// if the bottom inset is modified, window resizing stops working.
mInsets[0] = insets.left;
mInsets[1] = insets.top;
mInsets[2] = insets.right;
insets.left = 0;
insets.top = 0;
insets.right = 0;
}
return super.fitSystemWindows(insets);
}
@Override
public final WindowInsets onApplyWindowInsets(WindowInsets insets) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) {
mInsets[0] = insets.getSystemWindowInsetLeft();
mInsets[1] = insets.getSystemWindowInsetTop();
mInsets[2] = insets.getSystemWindowInsetRight();
return super.onApplyWindowInsets(insets.replaceSystemWindowInsets(0, 0, 0,
insets.getSystemWindowInsetBottom()));
} else {
return insets;
}
}
}
- ResizeLinearLayout
/**
* ResizeLinearLayout,配合fitsSystemWindows=true在根布局使用,解决软键盘布局调整的问题
*/
public class ResizeLinearLayout extends LinearLayout {
private int[] mInsets = new int[4];
public ResizeLinearLayout(Context context) {
super(context);
}
public ResizeLinearLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ResizeLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public ResizeLinearLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
protected final boolean fitSystemWindows(Rect insets) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
// Intentionally do not modify the bottom inset. For some reason,
// if the bottom inset is modified, window resizing stops working.
mInsets[0] = insets.left;
mInsets[1] = insets.top;
mInsets[2] = insets.right;
insets.left = 0;
insets.top = 0;
insets.right = 0;
}
return super.fitSystemWindows(insets);
}
@Override
public final WindowInsets onApplyWindowInsets(WindowInsets insets) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) {
mInsets[0] = insets.getSystemWindowInsetLeft();
mInsets[1] = insets.getSystemWindowInsetTop();
mInsets[2] = insets.getSystemWindowInsetRight();
return super.onApplyWindowInsets(insets.replaceSystemWindowInsets(0, 0, 0,
insets.getSystemWindowInsetBottom()));
} else {
return insets;
}
}
}