欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android webview嵌套一个h5的聊天页面,解决获取键盘的时候,布局整体上移

程序员文章站 2022-06-22 21:22:56
需求:一个Webview嵌套h5的聊天页面,当弹出键盘的时候,整体布局上移。如果不做处理,弹出的键盘会遮挡输入框,让人看不见输了什么内容。解决1:用AndroidBug5497Workaroundpublic class AndroidBug5497Workaround { private final Activity activity; // For more information, see https://code.google.com/p/android/issues/de...

需求:一个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);
            }
        }


    }

本文地址:https://blog.csdn.net/u013467495/article/details/107464951

相关标签: Android