Android uni app原生插件页面全屏Activity的键盘档住输入框或弹起键盘跳动问题

uni app 使用android原生插件页面EditText很不友好,点击EditText会出现键盘遮盖插件页面或者键盘频繁跳动问题。

解决方案:

  • 原理:获取当前页面底层content的高度activity.findViewById(android.R.id.content).getChildAt(0),监听底层content的变化,高度改变后,再触发布局更新
mContentView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
    public void onGlobalLayout() {//软键盘弹出、系统导航栏隐藏显示均会触发这里
        // 有变化就 setHeight(Integer);
    }
});
// 这里再做个防抖
private void setHeight(int height) {
    if (mFrameLayoutParams.height != height) {// 不必要的更新就不要了
        mContentView.postDelayed(() -> {
            // 键盘弹起uni已经更新了,就不用再次更新
            // uni 没更新,不更新,所以搞个延迟更新布局
            if (mFrameLayoutParams.height != height) {
                mFrameLayoutParams.height = height;
                mContentView.requestLayout();// 触发布局更新
            }
        }, 20);
    }
}

详细代码如下:

  • MyPandoraEntryActivity下延迟初始化单例
SoftKeyboardFixerForFullscreenUtil.assistActivity(this);//建议延迟1秒初始化
  • SoftKeyboardFixerForFullscreenUtil完整代码如下
/**
 * 解决全屏Activity的键盘档住输入框
 * 来自:https://blog.csdn.net/passerby_b/article/details/82686662
 * 注意:
 * 1.要在setContentView之后调用 assistActivity(activity)!
 * 2.要是横屏输入法不是满屏的,就需要自己适配了!
 * 3.自测没有发现问题,但无法100%保证兼容性~
 * 4.分屏模式下的处理,不知道会不会有其他问题,如果不是刚需,建议还是通过setSoftInputMode尝试调整~~~
 * <p>
 * 更新 Cooper 2018-9-13 13:27:59
 * 1.解决虚拟导航栏隐藏显示布局不自动适配的问题(三星Note8 8.0实测横屏竖屏都没问题,Vivo没有虚拟按键的机器6.0测试没有问题)
 * 2.解决分屏模式下不适配的问题(三星Note8 8.0实测横屏竖屏都没问题)
 * 3.优化代码
 * <p>
 * 参考:https://blog.csdn.net/smileiam/article/details/69055963
 * 参考:https://blog.csdn.net/auccy/article/details/80632429
 * 参考:https://github.com/yy1300326388/AndroidBarUtils/blob/master/app/src/main/java/cn/zsl/androidbarutils/utils/AndroidBarUtils.java
 * 其实最初的原版就是 AndroidBug5497Workaround ,但是原版考虑的不够全面,尤其是虚拟导航栏的问题,没有考虑进去
 * 参考:https://www.jianshu.com/p/a95a1b84da11
 */

public class SoftKeyboardFixerForFullscreenUtil {
    public static void assistActivity(Activity activity) {
        new SoftKeyboardFixerForFullscreenUtil(activity);
    }

    private final View mContentView;//我们设置的contentView
    private final FrameLayout.LayoutParams mFrameLayoutParams;//我们设置的contentView的layoutParams

//    private boolean isNavigationShowing = false;//没有用到这个
//    private boolean isFullscreenMode = false;//没有用到这个
//    private int barNavigationHeight = 0;//虚拟导航栏高度,没用
//    private int barNavigationWidth = 0;//虚拟导航栏宽度,没用

    private int barStatusHeight = 0;//状态栏高度

    private int lastUsableHeight = 0;//上一次的可用高度
//    private int lastUsableWidth = 0;//上一次的可用宽度

    private SoftKeyboardFixerForFullscreenUtil(final Activity activity) {
        //region 本来是想通过这个监听虚拟按键,结果发现这个回调比布局回调要晚,所以不用了。放在这里是为了给以后提供一些思路。
//        //1.为DecorView添加系统组件的可见变更事件
//        View decorView = activity.getWindow().getDecorView();
//        isNavigationShowing = ((decorView.getSystemUiVisibility() & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0);
//        isFullscreenMode = ((decorView.getSystemUiVisibility() & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0);//api 16以上
//        decorView.setOnSystemUiVisibilityChangeListener(new View.OnSystemUiVisibilityChangeListener() {//参考:https://blog.csdn.net/auccy/article/details/80632429
//            @Override
//            public void onSystemUiVisibilityChange(int visibility) {
//                if ((visibility & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0) {
//                    isNavigationShowing = true;
//                } else {
//                    isNavigationShowing = false;
//                }
//                if ((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) {
//                    isFullscreenMode = true;
//                } else {
//                    isFullscreenMode = true;
//                }
//            }
//        });
        //endregion
        //1.获取 状态栏 高度,获取 导航栏 高度、宽度(横屏用到的,可是横屏在手机上输入法会满屏,不知道不满屏的情况,所以不处理了,要是你遇到了,自行按照横屏的方式解决吧)
        barStatusHeight = getStatusBarHeight(activity);
        //barNavigationHeight = getNavigationBarHeight(activity);
        //barNavigationWidth = getNavigationBarWidth(activity);
        //2.找到Activity的最外层布局控件,它其实是一个DecorView,它所用的控件就是FrameLayout
        final FrameLayout content = activity.findViewById(android.R.id.content);
        //3.获取到setContentView放进去的View
        mContentView = content.getChildAt(0);
        //4.拿到我们设置的View的布局参数,主要是调整该参数来实现软键盘弹出上移
        mFrameLayoutParams = (FrameLayout.LayoutParams) mContentView.getLayoutParams();
        //5.给我们设置的View添加布局变动的监听,来实现布局动作(虚拟导航栏的弹出收起也会触发该监听!)
        mContentView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            public void onGlobalLayout() {//软键盘弹出、系统导航栏隐藏显示均会触发这里
//                int heightRoot = content.getRootView().getHeight();//包含虚拟按键的高度(如果有的话)
                int heightDecor = content.getHeight();//不含虚拟按键的高度,包含状态栏高度
//                Log.e("SoftKeyboardFi", "heightDecor  " + heightDecor);

                int usableHeight = computeUsableHeight();//我们setContentView设置的view的可用高度

                if (usableHeight != lastUsableHeight) {
                    lastUsableHeight = usableHeight;//防止重复变动

                    int heightDifference = heightDecor - usableHeight;

                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && activity.isInMultiWindowMode()) {//如果是分屏模式
                        if (heightDifference > 0) {//分屏模式,只要变动了就人为弹出键盘,因为分屏可能该Activity是在手机屏幕的上方,弹出输入法只是遮盖了一丁点~如果不合适,需要你自己适配了!
                            setHeight(heightDecor - heightDifference); //这里不能加状态栏高度哟~
                        } else {
                            setHeight(FrameLayout.LayoutParams.MATCH_PARENT);//还原默认高度,不能用计算的值,因为虚拟导航栏显示或者隐藏的时候也会改变高度
                        }
                    } else {
                        if (heightDifference > (heightDecor / 4)) {//高度变动超过decor的四分之一则认为是软键盘弹出事件,为什么不用屏幕高度呢?开始以为这样在分屏模式下也可以监听,但是实测不行。
                            Log.e("SoftKeyboardFi", "heightDifference > (heightDecor / 4) true");
                            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                                setHeight(heightDecor - heightDifference + barStatusHeight);//这里为什么要添加状态栏高度?
                            } else {
                                setHeight(heightDecor - heightDifference);//这里不添加状态栏高度?不懂为什么,原版如此,就先这样吧。遇到再说~
                            }
                            if (null != AppFloatView.instance) {
                                if (AppFloatView.get().appFloatIsShow()) {
                                    AppFloatView.get().hideFloat();
                                }
                            }
                        } else {
                            Log.e("SoftKeyboardFi", "heightDifference > (heightDecor / 4) false");
                            setHeight(FrameLayout.LayoutParams.MATCH_PARENT);//还原默认高度,不能用计算的值,因为虚拟导航栏显示或者隐藏的时候也会改变高度
                            if (null != AppFloatView.instance) {
                                if (!AppFloatView.get().appFloatIsShow() && !AppFloatView.IS_HIDE_LAYOUT) {
                                    AppFloatView.get().showFloat();
                                }
                            }
                        }
                    }
                }
            }
        });
    }

    private void setHeight(int height) {
        if (mFrameLayoutParams.height != height) {// 不必要的更新就不要了
            mContentView.postDelayed(() -> {
                // 键盘弹起uni已经更新了,就不用再次更新
                // uni 没更新,不更新,所以搞个延迟更新布局
                if (mFrameLayoutParams.height != height) {
                    mFrameLayoutParams.height = height;
                    mContentView.requestLayout();// 触发布局更新
                }
            }, 20);
        }
    }

    private int computeUsableHeight() {
        Rect r = new Rect();
        mContentView.getWindowVisibleDisplayFrame(r);
        // 全屏模式下:直接返回r.bottom,r.top其实是状态栏的高度
        return (r.bottom - r.top);
    }

    private int computeUsableWidth() {
        Rect r = new Rect();
        mContentView.getWindowVisibleDisplayFrame(r);
        // 全屏模式下:直接返回r.bottom,r.top其实是状态栏的高度//横屏就是宽度
        return (r.right - r.left);
    }


    //下面相关代码来自:https://github.com/yy1300326388/AndroidBarUtils/blob/master/app/src/main/java/cn/zsl/androidbarutils/utils/AndroidBarUtils.java
    //完整代码,全屏时有问题。
    private static final String STATUS_BAR_HEIGHT_RES_NAME = "status_bar_height";
    private static final String NAV_BAR_HEIGHT_RES_NAME = "navigation_bar_height";
    private static final String NAV_BAR_WIDTH_RES_NAME = "navigation_bar_width";

    /**
     * 获取状态栏高度
     *
     * @param context context
     * @return 状态栏高度
     */
    private static int getStatusBarHeight(Activity context) {
        // 获得状态栏高度
        return getBarHeight(context, STATUS_BAR_HEIGHT_RES_NAME);
    }

    /**
     * 获取导航栏高度
     *
     * @param activity activity
     * @return 导航栏高度
     */
    private static int getNavigationBarHeight(Activity activity) {
        if (hasNavBar(activity)) {
            // 获得导航栏高度
            return getBarHeight(activity, NAV_BAR_HEIGHT_RES_NAME);
        } else {
            return 0;
        }
    }

    /**
     * 获取横屏状态下导航栏的宽度
     *
     * @param activity activity
     * @return 导航栏的宽度
     */
    private static int getNavigationBarWidth(Activity activity) {
        if (hasNavBar(activity)) {
            // 获得导航栏高度
            return getBarHeight(activity, NAV_BAR_WIDTH_RES_NAME);
        } else {
            return 0;
        }
    }

    /**
     * 获取Bar高度
     *
     * @param context context
     * @param barName 名称
     * @return Bar高度
     */
    private static int getBarHeight(Context context, String barName) {
        // 获得状态栏高度
        int resourceId = context.getResources().getIdentifier(barName, "dimen", "android");
        return context.getResources().getDimensionPixelSize(resourceId);
    }

    /**
     * 是否有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;
    }
}

参考这篇文章,详细请参考:https://blog.csdn.net/passerby_b/article/details/82686662

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值