需求:一个Webview嵌套h5的聊天页面,当弹出键盘的时候,整体布局上移。
如果不做处理,弹出的键盘会遮挡输入框,让人看不见输了什么内容。
解决1:用AndroidBug5497Workaround
public class AndroidBug5497Workaround {
private final Activity activity;
// For more information, see https://code.google.com/p/android/issues/detail?id=5497
// To use this class, simply invoke assistActivity() on an Activity that already has its content view set.
public static void assistActivity(Activity activity) {
new AndroidBug5497Workaround(activity);
}
private View mChildOfContent;
private int usableHeightPrevious;
private FrameLayout.LayoutParams frameLayoutParams;
private int statusBarHeight;//状态栏高度
private AndroidBug5497Workaround(final Activity activity) {
this.activity = activity;
if (checkDeviceHasNavigationBar(activity)) {
//获取状态栏的高度
int resourceId = activity.getResources().getIdentifier("status_bar_height", "dimen", "android");
statusBarHeight = activity.getResources().getDimensionPixelSize(resourceId);
}
//1、找到Activity的最外层布局控件,它其实是一个DecorView,它所用的控件就是FrameLayout
FrameLayout content = (FrameLayout) activity.findViewById(android.R.id.content);
//2、获取到setContentView放进去的View
mChildOfContent = content.getChildAt(0);
//3、给Activity的xml布局设置View树监听,当布局有变化,如键盘弹出或收起时,都会回调此监听
mChildOfContent.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
//4、软键盘弹起会使GlobalLayout发生变化
public void onGlobalLayout() {
//5、当前布局发生变化时,对Activity的xml布局进行重绘
possiblyResizeChildOfContent(checkDeviceHasNavigationBar(activity));
}
});
//6、获取到Activity的xml布局的放置参数
frameLayoutParams = (FrameLayout.LayoutParams)
mChildOfContent.getLayoutParams();
}
/**
* 重新调整布局高度
* 获取界面可用高度,如果软键盘弹起后,Activity的xml布局可用高度需要减去键盘高度
*
* @param hasNav
*/
private void possiblyResizeChildOfContent(boolean hasNav) {
//1、获取当前界面可用高度,键盘弹起后,当前界面可用布局会减少键盘的高度
int usableHeightNow = computeUsableHeight(hasNav);
//2、如果当前可用高度和原始值不一样
if (usableHeightNow != usableHeightPrevious) {
//3、获取Activity中xml中布局在当前界面显示的高度
int usableHeightSansKeyboard;
if (hasNav)
usableHeightSansKeyboard = mChildOfContent.getHeight();//兼容华为等机型
else {
usableHeightSansKeyboard = mChildOfContent.getRootView().getHeight();
//这个判断是为了解决19之前的版本不支持沉浸式状态栏导致布局显示不完全的问题
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
Rect frame = new Rect();
activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(frame);
int statusBarHeight = frame.top;
usableHeightSansKeyboard -= statusBarHeight;
}
}
//4、Activity中xml布局的高度-当前可用高度
int heightDifference = usableHeightSansKeyboard - usableHeightNow;
//5、高度差大于屏幕1/4时,说明键盘弹出
if (heightDifference > (usableHeightSansKeyboard / 4)) {
// keyboard probably just became visible
// 6、键盘弹出了,Activity的xml布局高度应当减去键盘高度
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && hasNav) {
frameLayoutParams.height = usableHeightSansKeyboard - heightDifference + statusBarHeight;
} else {
frameLayoutParams.height = usableHeightSansKeyboard - heightDifference;
}
} else {
if (hasNav)
frameLayoutParams.height = usableHeightNow + statusBarHeight;
else
frameLayoutParams.height = usableHeightSansKeyboard;
}
//7、 重绘Activity的xml布局
mChildOfContent.requestLayout();
usableHeightPrevious = usableHeightNow;
}
}
/**
* 计算mChildOfContent可见高度 ** @return
*/
private int computeUsableHeight(boolean hasNav) {
if (hasNav) {
Rect r = new Rect();
mChildOfContent.getWindowVisibleDisplayFrame(r);
// 全屏模式下:直接返回r.bottom,r.top其实是状态栏的高度
if (r.top < statusBarHeight)
return r.bottom - statusBarHeight;
else
return r.bottom - r.top;
} else {
Rect frame = new Rect();
activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(frame);
int statusBarHeight = frame.top;
Rect r = new Rect();
mChildOfContent.getWindowVisibleDisplayFrame(r);
//这个判断是为了解决19之后的版本在弹出软键盘时,键盘和推上去的布局(adjustResize)之间有黑色区域的问题
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
return (r.bottom - r.top) + statusBarHeight;
}
return (r.bottom - r.top);
}
}
/**
* 通过"qemu.hw.mainkeys"判断是否存在NavigationBar
*
* @return 是否有NavigationBar
*/
private static boolean checkDeviceHasNavigationBar(Activity activity) {
boolean hasNavigationBar = false;
Resources rs = activity.getResources();
int id = rs.getIdentifier("config_showNavigationBar", "bool", "android");
if (id > 0) {
hasNavigationBar = rs.getBoolean(id);
}
try {
Class systemPropertiesClass = Class.forName("android.os.SystemProperties");
Method m = systemPropertiesClass.getMethod("get", String.class);
String navBarOverride = (String) m.invoke(systemPropertiesClass, "qemu.hw.mainkeys");
if ("1".equals(navBarOverride)) {
hasNavigationBar = false;
} else if ("0".equals(navBarOverride)) {
hasNavigationBar = true;
} else {
hasNavigationBar = hasNavBar(activity);
}
} catch (Exception e) {
}
return hasNavigationBar;
}
/**
* 根据屏幕真实宽高-可用宽高>0来判断是否存在NavigationBar
*
* @param activity 上下文
* @return 是否有NavigationBar
*/
private static boolean hasNavBar(Activity activity) {
WindowManager windowManager = activity.getWindowManager();
Display d = windowManager.getDefaultDisplay();
DisplayMetrics realDisplayMetrics = new DisplayMetrics();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
d.getRealMetrics(realDisplayMetrics);
}
int realHeight = realDisplayMetrics.heightPixels;
int realWidth = realDisplayMetrics.widthPixels;
DisplayMetrics displayMetrics = new DisplayMetrics();
d.getMetrics(displayMetrics);
int displayHeight = displayMetrics.heightPixels;
int displayWidth = displayMetrics.widthPixels;
return (realWidth - displayWidth) > 0 || (realHeight - displayHeight) > 0;
}
效果:虽然不会遮挡输入框了,但是整体的布局没有上移,只会看到输入框被顶了上去,整体布局没有移动。
解决2:用AndroidBug5497Workaround + CustomGlobalLayoutListener
上面已经有了AndroidBug5497Workaround的类,下面只贴一下CustomGlobalLayoutListener这个类
/**
*.
* 实现弹出软键盘时 整个布局向上平移,解决遮挡问题
* 在onCreate中添加监听,在onDestroy中remove监听
*/
public class CustomGlobalLayoutListener implements OnGlobalLayoutListener {
private Context mContext;
private View mRootView;
private View mScrollToView;
/**
* @param context context
* @param rootView 可以滚动的布局
* @param scrollToView 界面上被遮挡的位于最底部的布局(控件)
*/
public CustomGlobalLayoutListener(Context context, View rootView, View scrollToView) {
this.mContext = context;
this.mRootView = rootView;
this.mScrollToView = scrollToView;
}
@Override
public void onGlobalLayout() {
Rect rect = new Rect();
mRootView.getWindowVisibleDisplayFrame(rect);
int rootInvisibleHeight = mRootView.getRootView().getHeight() - rect.bottom;
if (rootInvisibleHeight > 100) {
int[] location = new int[2];
mScrollToView.getLocationInWindow(location);
int scrollHeight = (location[1] + mScrollToView.getHeight()) - rect.bottom;
mRootView.scrollTo(0, scrollHeight + Utils.dip2px(mContext, 30));
} else {
mRootView.scrollTo(0, 0);
}
}
}
实际的调用如下
初始化webview的时候
/**
* 当webview中的键盘弹起的时候,整个webview页面向上移动
*/
private void initWebViewLayout() {
AndroidBug5497Workaround.assistActivity(this);
customGlobalLayoutListener = new CustomGlobalLayoutListener(this, webView_common
, webView_common);
webView_common.getViewTreeObserver().addOnGlobalLayoutListener(customGlobalLayoutListener);
}
在生命周期onDestory方法里再调用
@Override
public void onDestroy() {
super.onDestroy();
if (customGlobalLayoutListener != null && webView_common != null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
webView_common.getViewTreeObserver().removeOnGlobalLayoutListener(customGlobalLayoutListener);
} else {
webView_common.getViewTreeObserver().removeGlobalOnLayoutListener(customGlobalLayoutListener);
}
}
}