android 沉浸状态栏下fitSystemWindow与键盘挡住输入框问题

android 沉浸状态栏下fitSystemWindow与键盘挡住输入框问题

在将状态栏改为沉浸时遇到了如下一个问题:fitsSystemWindows设置为true后,界面就无法全屏,因为顶部有一个状态栏高度的padding;不设置fitsSystemWindows,adjustResize模式无法用于沉浸全屏界面,导至输入框无法跟随键盘。

沉浸状态栏使用的工具:ImmersionBar

问题分析

  1. fitSystemWindow

    如果多个View设置了fitsSystemWindows=”true”,只有最外层view起作用,从最外层设置了fitsSystemWindows的view开始计算padding,如果在布局中不是最外层控件设置fitsSystemWindows=”true”, 那么设置的那个控件高度会多出一个状态栏高度。若有多个view设置了,因第一个view已经消耗掉insect,其他view设置了也会被系统忽略。

    详细内容见全屏、沉浸式、fitSystemWindow使用及原理分析:全方位控制“沉浸式”的实现

  2. 键盘挡住输入框问题

    方法一、在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无效的修复方案及踩坑指南

问题:

  1. 因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

你可能感兴趣的:(android技术)