美文网首页
Android 软键盘遮挡界面问题汇总

Android 软键盘遮挡界面问题汇总

作者: 果汁味Studio | 来源:发表于2023-03-09 16:55 被阅读0次

    场景1

    问题描述

    常规的,当点击EditText输入框时,软键盘弹出,但键盘会遮挡EditText显示。

    解决方案

    AndroidManifest文件中,Activity新增配置android:windowSoftInputMode,设置值为adjustPanadjustResize,如下:

    <activity
        android:name="xxx"
        android:windowSoftInputMode="adjustPan"  />
    

    adjustPan和 adjustResize的区别

    • adjustPan配置该参数,整个界面会向上平移,显示输入框,但不改变界面的布局。
    • adjustResize配置该参数,系统会重新计算除去软键盘后的界面尺寸,用更少的界面区域显示内容,输入框也在其中。



    场景2

    问题描述

    混合开发中,应用在非全屏模式下,WebView加载网页,点击输入框,软键盘遮挡问题。

    解决方案

    操作方法同上,但是只支持设置值为adjustResize,设置 adjustPan无效。


    场景3

    问题描述

    • 混合开发中,应用在全屏模式下(Application或activity使用Fullscreen主题、沉浸式状态栏、Immersive Mode等),WebView加载网页,点击输入框,软键盘遮挡问题。
    • android:windowSoftInputMode设置值为adjustPanadjustResize均无效。
    • 这是系统级别的一个坑。

    解决方案

    方案1

    当一个Activity中包含WebView控件时,尽量避免使用全景模式。(当然这个方法治标不治本)

    方案2

    stackflow上有个大牛提供了解决方案。(stackflow地址

    具体代码如下,

    import android.app.Activity;
    import android.graphics.Rect;
    import android.os.Build;
    import android.view.View;
    import android.view.ViewTreeObserver;
    import android.widget.FrameLayout;
    
    /**
     * 修复WebView全屏模式下,软键盘遮挡问题
     */
    public final class AndroidBug5497Workaround {
        private View mChildOfContent;
        private int usableHeightPrevious;
        private FrameLayout.LayoutParams frameLayoutParams;
        private int contentHeight;
        private boolean isFirst = true;
        private int statusBarHeight;
    
        public static void assistActivity(Activity activity) {
            new AndroidBug5497Workaround(activity);
        }
    
        private AndroidBug5497Workaround(Activity activity) {
            //获取状态栏的高度
            int resourceId = activity.getResources().getIdentifier("status_bar_height", "dimen", "android");
            statusBarHeight = activity.getResources().getDimensionPixelSize(resourceId);
            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 usableHeightSansKeyboard = mChildOfContent.getRootView().getHeight();
                int heightDifference = usableHeightSansKeyboard - usableHeightNow;
                if (heightDifference > (usableHeightSansKeyboard / 4)) {
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                        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);
        }
    }
    
    • 将该类新增到项目中,并在对应ActivityonCreate()方法中调用即可。
            AndroidBug5497Workaround.assistActivity(this);
    

    AndroidBug5497Workaround类解析:

    1. 通过findViewById(android.R.id.content)获取Activity界面开发者可控区域的根View。(全屏模式下,即为全部屏幕区域,非全屏模式下为除状态栏外区域

    2. mChildOfContent = content.getChildAt(0);,获取 Activity布局的 View(即通过 setContentView()设置的 View)。

    3. View.getViewTreeObserver()获取一个专门监听当前View树变化的观察者对象

    4. 由于软键盘的弹出,会使View树的全局布局(GlobalLayout)发生变化,所以给当前View树变化的观察者对象绑定监听addOnGlobalLayoutListener

    5. 一旦触发addOnGlobalLayoutListener监听,主要则是调用possiblyResizeChildOfContent()方法,重置界面变化后可用区域。

    6. possiblyResizeChildOfContent()方法中,heightDifference > (usableHeightSansKeyboard / 4)这个判断只有界面的高度变化超过1/4,才重置高度,确保响应软键盘的弹出,排除其他界面变化情况,

    7. computeUsableHeight()则是计算除去键盘后重新调整布局的可用高度。


    软键盘遮挡界面问题总结

    Android输入框软键盘遮挡问题,通常有以下三种情况:

    • 在无WebView的界面下:
      按需求配置android:windowSoftInputMode,为adjustPanadjustResize

    • 有WebView的界面下:
      非全屏模式:
      配置android:windowSoftInputModeadjustResize
      全屏模式:
      项目新增一个AndroidBug5497Workaround类,完美解决。

    参考文章

    相关文章

      网友评论

          本文标题:Android 软键盘遮挡界面问题汇总

          本文链接:https://www.haomeiwen.com/subject/mdwmldtx.html