【WebView为什么没有在软键盘弹出时更新布局】
默认Activity情况下,软键盘弹出时,通过给DecorView的LinearLayout添加"layout_margin_bottom=键盘高度",实现R.id.content避开软键盘。
如果你的WebView出现输入框被软键盘遮挡问题,需要查看下为什么上述逻辑没有生效?
我在项目上遇到的原因是,WebView页面设置了window布局延伸到状态栏,导致 adjustResize 无法生效,窗口尺寸也就不会适配软键盘。
// 布局延伸到状态栏
int uiFlags = View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
mWindow.getDecorView().setSystemUiVisibility(uiFlags);
【如何手动处理软键盘更新布局】
通用解决方案是 addOnGlobalLayoutListener 来监听,网上有现成的方案叫 AndroidBug5497Workaround
我采用了这个方案,差点导致了线上Live Issue。原因是这个方案的适用场景是全屏显示,既沉浸到状态栏,也沉浸到了底部虚拟按键高度,导致WebView被底部虚拟按键吃进去一部分,最下面的内容不可见!!!每一个方案都有它适用的场景,不理解清楚为什么要这么写,不要随便拿来使用哈。
这里需要对DecorView的布局有一个详细的了解。
DecorView 中添加的第一个View是 mContentRoot,mContentRoot 比较常见的布局文件是 R.layout.screen_simple。PhoneWindow 的
mContentParent
是 DecorView 布局中对应 ID 为“content”的视图。除了视图“content”之外,DecorView 的视图还有 ID 为“title” 的 WindowTitle。setContentView 时,创建 subDecor。subDecor 布局通常是 R.layout.abc_screen_simple,它的 R.id.action_bar_activity_content 会被重命名为 android.R.id.content(原来DecorView中的android.R.id.content被改名了),subDecor 创建后被 addView 到 PhoneWindow#mContentParent。contentView的内容,又会addView到 R.id.action_bar_activity_content。(AppCompatDelegateImpl#createSubDecor())。
状态栏和导航栏是DecorView中额外addView的两个子View(updateColorViewInt())
DecorView的三个子View:
- LinearLayout,上顶到窗口(paddingTop让开状态栏),下挨着导航栏(layout_marginBottom)
- View 状态栏,上
- View 导航栏,下
ImmersionBar 开源库的实现可以拿来参考,精简版实现如下:
class WebSoftInputHelper {
// For more information, see https://issuetracker.google.com/issues/36911528
public static void assistActivity(@NonNull Activity activity) {
new WebSoftInputHelper(activity);
}
private final View mContentParent;
View mDecorView;
private int keyboardHeightPrevious;
private WebSoftInputHelper(@NonNull Activity activity) {
mDecorView = activity.getWindow().getDecorView();
mContentParent = mDecorView.findViewById(android.R.id.content);
mDecorView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
public void onGlobalLayout() {
possiblyResizeChildOfContent();
}
});
}
private void possiblyResizeChildOfContent() {
Rect r = new Rect();
mContentParent.getWindowVisibleDisplayFrame(r);
int keyboardHeight = mContentParent.getBottom() - r.bottom;
if (keyboardHeight != keyboardHeightPrevious) {
mContentParent.setPadding(mContentParent.getPaddingLeft(),
mContentParent.getPaddingTop(),
mContentParent.getPaddingRight(),
keyboardHeight);
keyboardHeightPrevious = keyboardHeight;
}
}
}
解释下 getWindowVisibleDisplayFrame 方法:
- 不管通过哪个View调用,最终会调用到所在Window的可视化区域
- 这里暂时不计算状态栏对top的影响
- 软键盘弹出的时候,bottom = 屏幕高度 - 软键盘 - 虚拟按键,mContentView.getHeight() - rect.bottom = 软键盘
- 软键盘关闭时,bottom = 屏幕高度 - 虚拟按键,mContentView.getHeight() - rect.bottom = 0