在将状态栏改为沉浸时遇到了如下一个问题:fitsSystemWindows设置为true后,界面就无法全屏,因为顶部有一个状态栏高度的padding;不设置fitsSystemWindows,adjustResize模式无法用于沉浸全屏界面,导至输入框无法跟随键盘。
沉浸状态栏使用的工具:ImmersionBar
fitSystemWindow
如果多个View设置了fitsSystemWindows=”true”
,只有最外层view起作用,从最外层设置了fitsSystemWindows
的view开始计算padding,如果在布局中不是最外层控件设置fitsSystemWindows=”true”
, 那么设置的那个控件高度会多出一个状态栏高度。若有多个view设置了,因第一个view已经消耗掉insect,其他view设置了也会被系统忽略。
详细内容见全屏、沉浸式、fitSystemWindow使用及原理分析:全方位控制“沉浸式”的实现
键盘挡住输入框问题
方法一、在AndroidManifest.xml对应的Activity里添加windowSoftInputMode
属性
adjustResize:调整activity主窗口的尺寸来为屏幕上的软键盘腾出空间
adjustPan:自动平移窗口的内容,使当前焦点永远不被键盘遮盖,让用户始终都能看到其输入的内容。只有关闭软键盘 才能看到因平移被遮盖的内容。
方法二、在布局最外层使用ScrollView
详细内容见android全屏/沉浸式状态栏下,各种键盘挡住输入框解决办法
去除fitSystemWindow,然后使用adjustResize以保证输入框跟随软键盘,所以现在只要解决一个问题:adjustResize在全屏时失效。
最后通过监听addOnGlobalLayoutListener
在输入法弹出时改变rootView或DecorView的高度,使view高度=屏幕高度-输入法高度。代码如下:
import android.app.Activity;
import android.graphics.Rect;
import android.os.Build;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
public class FullScreenInputWorkaround {
// 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.
private static final String TAG = "AndroidBug5497Workaround";
public static FullScreenInputWorkaround assistActivity(Activity activity, View contentView, InputShowListener inputShowListener) {
return new FullScreenInputWorkaround(activity, contentView, inputShowListener);
}
private Activity activity;
private View mChildOfContent;
private int usableHeightPrevious;
private ViewGroup.LayoutParams layoutParams;
private ViewTreeObserver.OnGlobalLayoutListener listener;
private FullScreenInputWorkaround(Activity activity, View contentView, InputShowListener inputShowListener) {
this.activity = activity;
this.inputShowListener = inputShowListener;
mChildOfContent = contentView;
mChildOfContent.getViewTreeObserver().addOnGlobalLayoutListener(listener = new ViewTreeObserver.OnGlobalLayoutListener() {
public void onGlobalLayout() {
possiblyResizeChildOfContent();
}
});
layoutParams = mChildOfContent.getLayoutParams();
}
private void possiblyResizeChildOfContent() {
int usableHeightNow = computeUsableHeight();
if (usableHeightNow != usableHeightPrevious) {
int usableHeightSansKeyboard = mChildOfContent.getRootView().getHeight();
int heightDifference = usableHeightSansKeyboard - usableHeightNow;
if (heightDifference > (usableHeightSansKeyboard / 4)) {
// keyboard probably just became visible
layoutParams.height = usableHeightSansKeyboard - heightDifference;
if (inputShowListener != null) {
inputShowListener.inputShow(true);
}
} else {
// keyboard probably just became hidden
layoutParams.height = usableHeightSansKeyboard;
if (inputShowListener != null) {
inputShowListener.inputShow(false);
}
}
mChildOfContent.requestLayout();
usableHeightPrevious = usableHeightNow;
}
}
private int computeUsableHeight() {
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);
}
public void finish() {
if(mChildOfContent != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
mChildOfContent.getViewTreeObserver().removeOnGlobalLayoutListener(listener);
}
}
private InputShowListener inputShowListener;
public interface InputShowListener {
void inputShow(boolean show);
}
}
详细内容见Android全屏状态下弹出输入法adjustResize无效的修复方案及踩坑指南
因addOnGlobalLayoutListener监听每次都会改变根布局高度,会导致布局与虚拟导航栏重叠。
解决方案:在修改布局高度时判断是否要减去导航栏高度。
修改后的代码如下:
public class FullScreenInputWorkaround {
// 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.
private static final String TAG = "AndroidBug5497Workaround";
public static FullScreenInputWorkaround assistActivity(Activity activity, View contentView, InputShowListener inputShowListener) {
return new FullScreenInputWorkaround(activity, contentView, inputShowListener);
}
private Activity activity;
private View mChildOfContent;
private int usableHeightPrevious;
private ViewGroup.LayoutParams layoutParams;
private ViewTreeObserver.OnGlobalLayoutListener listener;
private FullScreenInputWorkaround(Activity activity, View contentView, InputShowListener inputShowListener) {
this.activity = activity;
this.inputShowListener = inputShowListener;
mChildOfContent = contentView;
mChildOfContent.getViewTreeObserver().addOnGlobalLayoutListener(listener = new ViewTreeObserver.OnGlobalLayoutListener() {
public void onGlobalLayout() {
possiblyResizeChildOfContent();
}
});
layoutParams = mChildOfContent.getLayoutParams();
}
private void possiblyResizeChildOfContent() {
int usableHeightNow = computeUsableHeight();
if (usableHeightNow != usableHeightPrevious) {
int usableHeightSansKeyboard = mChildOfContent.getRootView().getHeight();
int heightDifference = usableHeightSansKeyboard - usableHeightNow;
if (heightDifference > (usableHeightSansKeyboard / 4)) {
// keyboard probably just became visible
layoutParams.height = usableHeightSansKeyboard - heightDifference;
if (inputShowListener != null) {
inputShowListener.inputShow(true);
}
} else {
// keyboard probably just became hidden
layoutParams.height = usableHeightSansKeyboard - (NavigationBoomUtils.navigationBarExist(activity) && heightDifference != 0 ? NavigationBoomUtils.getNavigationBarHeight(activity) : 0);
if (inputShowListener != null) {
inputShowListener.inputShow(false);
}
}
if (mChildOfContent != null) {
mChildOfContent.requestLayout();
}
usableHeightPrevious = usableHeightNow;
}
}
private int computeUsableHeight() {
Rect frame = new Rect();
if (activity != null) {
activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(frame);
}
int statusBarHeight = frame.top;
Rect r = new Rect();
if (mChildOfContent != null) {
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);
}
public void finish() {
if (mChildOfContent != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
//计算完成,要移除监听
mChildOfContent.getViewTreeObserver().removeOnGlobalLayoutListener(listener);
}
}
private InputShowListener inputShowListener;
public interface InputShowListener {
void inputShow(boolean show);
}
}
NavigationBoomUtils代码如下:
public class NavigationBoomUtils {
private static final String TAG = NavigationBoomUtils.class.getSimpleName();
private NavigationBoomUtils() {
throw new RuntimeException("NavUtils cannot be initialized!");
}
/**
* 获取底部虚拟导航栏高度
*
* @param activity
* @return
*/
public static int getNavigationBarHeight(Context activity) {
try {
boolean hasNavigationBar = navigationBarExist(scanForActivity(activity)) && !vivoNavigationGestureEnabled(activity);
if (!hasNavigationBar) {//如果不含有虚拟导航栏,则返回高度值0
return 0;
}
Resources resources = activity.getResources();
int resourceId = resources.getIdentifier("navigation_bar_height",
"dimen", "android");
//获取NavigationBar的高度
return resources.getDimensionPixelSize(resourceId);
}catch (Throwable e){
}
return 0;
}
/**
* 通过获取不同状态的屏幕高度对比判断是否有NavigationBar
* https://blog.csdn.net/u010042660/article/details/51491572
* https://blog.csdn.net/android_zhengyongbo/article/details/68941464
*/
public static boolean navigationBarExist(Activity activity) {
WindowManager windowManager = activity.getWindowManager();
Display d = windowManager.getDefaultDisplay();
DisplayMetrics realDisplayMetrics = new DisplayMetrics();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
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;
}
/**
* 解决java.lang.ClassCastException: android.view.ContextThemeWrapper cannot be cast to android.app.Activity问题
* https://blog.csdn.net/yaphetzhao/article/details/49639097
*/
public static Activity scanForActivity(Context cont) {
if (cont == null)
return null;
else if (cont instanceof Activity)
return (Activity) cont;
else if (cont instanceof ContextWrapper)
return scanForActivity(((ContextWrapper) cont).getBaseContext());
return null;
}
/**
* 获取vivo手机设置中的"navigation_gesture_on"值,判断当前系统是使用导航键还是手势导航操作
*
* @param context app Context
* @return false 表示使用的是虚拟导航键(NavigationBar), true 表示使用的是手势, 默认是false
* https://blog.csdn.net/weelyy/article/details/79284332#更换部分被拉伸的图片资源文件
*/
public static boolean vivoNavigationGestureEnabled(Context context) {
int val = Settings.Secure.getInt(context.getContentResolver(), "navigation_gesture_on", 0);
return val != 0;
}
nativationBottomUtils参考 :https://www.cnblogs.com/whycxb/p/7635745.html
参考:https://www.jianshu.com/p/a62351cea3ed