安卓键盘适配弹起效果及弹窗键盘适配

330 阅读7分钟

键盘弹起效果

诉求:需要监听软键盘弹起,并根据键盘高度变化来调整卡片高度

现状:安卓目前并没有官方监听软键盘弹起以及弹起高度的API

功能设计:

  1. 给页面窗口添加一个高度为屏幕高度,宽度为0的PopupWindow,实现OnGlobalLayoutListener 接口监听键盘是否弹起且返回弹起高度
  2. 禁止内容输入区域EditText获取焦点自动拉起键盘,外层做点击监听,自己拉起键盘
public class KeyBoardHeightListenerView extends PopupWindow implements ViewTreeObserver.OnGlobalLayoutListener {
    private final static String TAG = "KeyBoardHeightListenerView";
    private Activity mActivity;
    private View rootView;
    private HeightListener listener;
    private int heightMax; // 记录popup内容区的最大高度
    private Rect rect;
    private int preKeyboardHeight = 0;
    private int statusBarHeight;
    private int deviceHeight;
    private boolean isIntercept = true;

    public KeyBoardHeightListenerView(Activity activity) {
        super(activity);
        this.mActivity = activity;
        try {
            // 基础配置
            rootView = new View(activity);
            setContentView(rootView);

            // 监听全局Layout变化
            rootView.getViewTreeObserver().addOnGlobalLayoutListener(this);
            setBackgroundDrawable(new ColorDrawable(0));

            // 设置宽度为0,高度为全屏
            setWidth(0);
            setHeight(WindowManager.LayoutParams.MATCH_PARENT);

            // 设置键盘弹出方式
            setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
            setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED);

            int resourceId = mActivity.getResources().getIdentifier("status_bar_height", "dimen", "android");
            if (resourceId > 0) {
                statusBarHeight = mActivity.getResources().getDimensionPixelSize(resourceId);
                AliUserLog.i(TAG, "statusBarHeight = " + statusBarHeight);
            }
            deviceHeight = DeviceInfo.getInstance().getHeightPix();
            AliUserLog.i(TAG, "deviceHeight = " + deviceHeight);
        } catch (Exception e) {
            AliUserLog.e(TAG, e);
        }
    }

    public KeyBoardHeightListenerView init() {
        if (!isShowing()) {
            final View view = mActivity.getWindow().getDecorView();
            // 延迟加载popup 如果不加延迟就会报错因为根view还没渲染
            view.post(new Runnable() {
                @Override
                public void run() {
                    try {
                        if (!mActivity.isFinishing()) {
                            showAtLocation(view, Gravity.NO_GRAVITY, 0, 0);
                        }
                    } catch (Exception e) {
                        AliUserLog.e(TAG, e);
                    }
                }
            });
        }
        return this;
    }

    @Override
    public void onGlobalLayout() {
        if (null == rect) {
            rect = new Rect();
        }
        rootView.getWindowVisibleDisplayFrame(rect);
        if (rect.bottom > heightMax) {
            heightMax = rect.bottom;
        }

        // 两者的差值就是键盘的高度
        int keyboardHeight = heightMax - rect.bottom;
        AliUserLog.i(TAG, "heightMax = " + heightMax + " ,rect.bottom = " + rect.bottom + " ,originKeyboardHeight = " + keyboardHeight);
        if (isIntercept && keyboardHeight == 0) {//拦截第一次为0的情况
            return;
        }
        isIntercept = false;
        //放过0-150的情况 这样可以进行数据重置
//        if (keyboardHeight < 150) {//去杂数据 状态栏高度变化导致的回调 0-150之间过滤掉
//            keyboardHeight = 0;
//        } else
        if (keyboardHeight > deviceHeight * 0.75) {//兼容某些小米及三星手机弹窗是实际二倍问题
            //键盘高度大于屏幕的0.75 减半 防止测量错误
            keyboardHeight = keyboardHeight / 2;
        } else if (keyboardHeight >= deviceHeight * 0.60) {//兼容登录卡片比实际高的问题
            //键盘高度大于屏幕的0.6 防止测量错误
            keyboardHeight = (deviceHeight * 3) / 5 -  CommonUtil.dp2Px(mActivity, 132);
        } else if (150 < keyboardHeight && keyboardHeight < deviceHeight * 0.4) {//兼容登录卡片比实际低的问题
            //键盘高度小于屏幕的0.4 防止测量错误
            keyboardHeight = (deviceHeight * 2) / 5;
        }

        AliUserLog.i(TAG, "preKeyboardHeight = " + preKeyboardHeight + " ,keyboardHeight = " + keyboardHeight);

        if (preKeyboardHeight == keyboardHeight) {
            //去重 高度相同则不处理
            return;
        }

        preKeyboardHeight = keyboardHeight;

        if (preKeyboardHeight > 0 && preKeyboardHeight < 150) {
            keyboardHeight = keyboardHeight - preKeyboardHeight;
        }

        if (keyboardHeight == 0) {//键盘消失后 保证监控到键盘弹起
            setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
        } else {//键盘弹起则要保证键盘驻留 不被锁屏隐藏
            setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED);
        }

        if (listener != null) {
            listener.onHeightChanged(keyboardHeight);
        }
    }


    public KeyBoardHeightListenerView setHeightListener(HeightListener listener) {
        this.listener = listener;
        return this;
    }

    public interface HeightListener {
        void onHeightChanged(int height);
    }
}

监听方法实现

/**
 * 适配键盘 防止遮挡
 * 监听键盘弹起显示背景阴影
 */
public void initWrapper() {
    keyBoardHeightListenerView = new KeyBoardHeightListenerView(getActivity())
            .init().setHeightListener(new KeyBoardHeightListenerView.HeightListener() {
                @Override
                public void onHeightChanged(int height) {//依赖于软键盘是否弹起
                    LoggerFactory.getTraceLogger().info(getCurrentLoginPageState() + " - " + TAG, "onHeightChanged height: " +
                            height + " ,keyBoardIsShow:" + keyBoardIsShow);
                    if (null == mLoginCardView) {
                        LoggerFactory.getTraceLogger().info(getCurrentLoginPageState() + " - " + TAG, "mLoginCardView is null");
                        return;
                    }
                    if (!isVisible) {
                        AliUserLog.i(getCurrentLoginPageState() + " - " + TAG, "fragment 不可见,不响应键盘事件");
                        return;
                    }
                    if (getDropListenerData()) {//延时
                        AliUserLog.i(getCurrentLoginPageState() + " - " + TAG, "阻塞");
                        return;
                    }
                    if (height > 150) {
                        keyBoardIsShow = true;
                        mLoginCardView.reSetKeyBoardState(true);
                        reSetLoginCardViewHeightWrapper(height, false);
                    } else {
                        keyBoardIsShow = false;
                        mLoginCardView.reSetKeyBoardState(false);
                    }
                }
            });
}

调整卡片高度方法实现

/**
 * 接受外部调用 和软键盘可以无关
 *
 * @param keyBoardHeight
 * @param force
 */
public void reSetLoginCardViewHeightWrapper(final int keyBoardHeight, final boolean force) {
    try {
        if (loginCardIsMoving || null == mActivity) {//正在移动不用响应
            return;
        }
        mActivity.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                if (keyBoardHeight > 0 && loginCardViewOriginHeight == 0) {//第一次展示
                    reSetLoginCardViewHeight(true, keyBoardHeight, force);
                } else if (keyBoardHeight > 0) {//调整再次展示
                    reSetLoginCardViewHeight(true, keyBoardHeight, force);
                } else if (keyBoardHeight <= 0 && loginCardViewOriginHeight > 0) {//隐藏
                    reSetLoginCardViewHeight(false, keyBoardHeight, force);
                    hideSoftKeyBoard();
                }
            }
        });
    } catch (Exception e) {
        AliUserLog.e(getCurrentLoginPageState() + " - " + TAG, "reSetLoginCardViewHeightWrapper,e = ", e);
    }
}

卡片外层包装方法

/**
 * 主动隐藏软键盘
 */
public void hideSoftKeyBoard() {
    AliUserLog.i(getCurrentLoginPageState() + " - " + TAG, "hideSoftKeyBoard");
    keyBoardIsShow = false;
    KeyBoardUtil.closeInputMethod(mLoginView);
}

/**
 * 接受外部调用 和软键盘可以无关
 *
 * @param keyBoardHeight
 * @param force
 */
public void reSetLoginCardViewHeightWrapper(final int keyBoardHeight, final boolean force) {
    try {
        if (loginCardIsMoving || null == mActivity) {//正在移动不用响应
            return;
        }
        mActivity.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                if (keyBoardHeight > 0 && loginCardViewOriginHeight == 0) {//第一次展示
                    reSetLoginCardViewHeight(true, keyBoardHeight, force);
                } else if (keyBoardHeight > 0) {//调整再次展示
                    reSetLoginCardViewHeight(true, keyBoardHeight, force);
                } else if (keyBoardHeight <= 0 && loginCardViewOriginHeight > 0) {//隐藏
                    reSetLoginCardViewHeight(false, keyBoardHeight, force);
                    hideSoftKeyBoard();
                }
            }
        });
    } catch (Exception e) {
        AliUserLog.e(getCurrentLoginPageState() + " - " + TAG, "reSetLoginCardViewHeightWrapper,e = ", e);
    }
}

拉起卡片动画


/**
 * 重制登录卡片高度
 * 根据主按钮进行换算
 *
 * @param isPullUp       是否拉起键盘
 * @param keyBoardHeight 键盘高度
 * @param force          强制弹起
 */
private void reSetLoginCardViewHeight(boolean isPullUp, final int keyBoardHeight, boolean force) {
    reSetLoginCardViewHeight(isPullUp, keyBoardHeight, force, true);
}

/**
 * 重制登录卡片高度
 * 根据主按钮进行换算
 *
 * @param isPullUp       是否拉起键盘
 * @param keyBoardHeight 键盘高度
 * @param force          强制弹起
 * @param hasAnimation    有无动画
 */
private void reSetLoginCardViewHeight(boolean isPullUp, final int keyBoardHeight, boolean force, boolean hasAnimation) {
    try {
        AliUserLog.i(getCurrentLoginPageState() + " - " + TAG, "reSetLoginCardViewHeight");
        if (!isVisible) {
            return;
        }
        if (isPullUp && mLoginCardView.shouldPullUpWhenKeyboardShow()) {
            showLoginViewMask();
            int anchorKeyBoardHeight = CommonUtil.dp2Px(mActivity, 80);
            if (mLoginCardView.bottomContainer.getVisibility() == View.VISIBLE) {
                anchorKeyBoardHeight = CommonUtil.dp2Px(mActivity, 60);
            }
            if (keyBoardHeight <= anchorKeyBoardHeight) {
                return;
            }
            final int modifySize = keyBoardHeight - anchorKeyBoardHeight;
            if (loginCardViewOriginHeight == 0) {
                loginCardViewOriginHeight = mLoginCardView.loginCardView.getLayoutParams().height;
            }
            int loginCardViewPreHeight = mLoginCardView.loginCardView.getLayoutParams().height;
            AliUserLog.i(getCurrentLoginPageState() + " - " + TAG, "loginCardViewOriginHeight = " + loginCardViewOriginHeight +
                    " ,loginCardPullUpHeight = " + loginCardPullUpHeight + " ,modifySize = " + modifySize +
                    " ,loginCardViewPreHeight = " + loginCardViewPreHeight);
            if (loginCardPullUpHeight >= modifySize) {//防止向下调整
                return;
            }
            if (!hasAnimation) {
                ViewGroup.LayoutParams layoutParams = mLoginCardView.loginCardView.getLayoutParams();
                layoutParams.height = loginCardViewOriginHeight + modifySize;
                mLoginCardView.loginCardView.setLayoutParams(layoutParams);
                ViewGroup.LayoutParams btnLayoutParams = mLoginCardView.getMainButton().getLayoutParams();
                ((RelativeLayout.LayoutParams) btnLayoutParams).bottomMargin =
                        ((RelativeLayout.LayoutParams) btnLayoutParams).bottomMargin + modifySize;
                mLoginCardView.getMainButton().setLayoutParams(btnLayoutParams);
                loginCardPullUpHeight = modifySize;
                return;
            }

            loginCardIsMoving = true;
            // 平移 translation
            upValueAnimator = ValueAnimator.ofInt(loginCardViewPreHeight,
                    modifySize - loginCardPullUpHeight + loginCardViewPreHeight);
            upValueAnimator.setDuration(300);
            upValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator valueAnimator) {
                    try {
                        int val = (Integer) valueAnimator.getAnimatedValue();
                        ViewGroup.LayoutParams layoutParams = mLoginCardView.loginCardView.getLayoutParams();
                        int changeVal = val - layoutParams.height;
                        layoutParams.height = val;
                        mLoginCardView.loginCardView.setLayoutParams(layoutParams);
                        ViewGroup.LayoutParams btnLayoutParams = mLoginCardView.getMainButton().getLayoutParams();
                        ((RelativeLayout.LayoutParams) btnLayoutParams).bottomMargin =
                                ((RelativeLayout.LayoutParams) btnLayoutParams).bottomMargin + changeVal;
                        mLoginCardView.getMainButton().setLayoutParams(btnLayoutParams);
                    } catch (Exception e) {
                        LoggerFactory.getTraceLogger().error(getCurrentLoginPageState() + " - " + TAG, "reSetLoginCardViewHeight e = ", e);
                    }

                }
            });
            upValueAnimator.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationStart(Animator animation) {
                    super.onAnimationStart(animation);
                    loginCardPullUpHeight = modifySize;
                    onPullUpStart();
                }

                @Override
                public void onAnimationEnd(Animator animation) {
                    super.onAnimationEnd(animation);
                    loginCardIsMoving = false;
                    if (null != upValueAnimator) {
                        upValueAnimator.cancel();
                        upValueAnimator = null;
                    }
                    onPullUpEnd();
                }

            });
            upValueAnimator.start();
        } else if (!isPullUp && (force || mLoginCardView.shouldPullDownWhenKeyboardHide()) && loginCardViewOriginHeight > 0) {
            hideLoginViewMask();
            loginCardIsMoving = true;
            downValueAnimator = ValueAnimator.ofInt(
                    mLoginCardView.loginCardView.getLayoutParams().height, loginCardViewOriginHeight);
            downValueAnimator.setDuration(300);
            downValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator valueAnimator) {
                    int val = (Integer) valueAnimator.getAnimatedValue();
                    ViewGroup.LayoutParams layoutParams = mLoginCardView.loginCardView.getLayoutParams();
                    int changeVal = layoutParams.height - val;
                    layoutParams.height = val;
                    mLoginCardView.loginCardView.setLayoutParams(layoutParams);

                    ViewGroup.LayoutParams btnLayoutParams = mLoginCardView.getMainButton().getLayoutParams();
                    ((RelativeLayout.LayoutParams) btnLayoutParams).bottomMargin =
                            ((RelativeLayout.LayoutParams) btnLayoutParams).bottomMargin - changeVal;
                    mLoginCardView.getMainButton().setLayoutParams(btnLayoutParams);
                }
            });
            downValueAnimator.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationStart(Animator animation) {
                    super.onAnimationStart(animation);
                    onPullDownStart();
                }

                @Override
                public void onAnimationEnd(Animator animation) {
                    super.onAnimationEnd(animation);
                    loginCardPullUpHeight = 0;
                    loginCardViewOriginHeight = 0;
                    loginCardIsMoving = false;
                    if (isWaitKeyboardPullDown) {
                        isWaitKeyboardPullDown = false;
                        changeLoginView(tempLoginBaseView, tempLoginBasePresenter, tempIsAliPayLoginPage);
                    }
                    if (null != downValueAnimator) {
                        downValueAnimator.cancel();
                        downValueAnimator = null;
                    }

                    onPullDownEnd();
                }
            });

            downValueAnimator.start();
        }
    } catch (Exception e) {
        AliUserLog.e(getCurrentLoginPageState() + " - " + TAG, "reSetLoginCardViewHeight,e = ", e);
    }
}

键盘上升及下滑事件接口

/**
 * 卡片上升开始
 */
protected void onPullUpStart() {
    if (null != mLoginCardView) {
        mLoginCardView.onPullUpStart();//LoginBaseView
    }
    if (null != mLoginCardView && null != mLoginCardView.loginBasePresenter) {
        mLoginCardView.loginBasePresenter.onPullUpStart();//LoginBasePresenter
    }
}

/**
 * 卡片上升完成
 */
protected void onPullUpEnd() {
    if (null != mLoginCardView) {
        mLoginCardView.onPullUpEnd();//LoginBaseView
    }
    if (null != mLoginCardView && null != mLoginCardView.loginBasePresenter) {
        mLoginCardView.loginBasePresenter.onPullUpEnd();//LoginBasePresenter
    }
}

/**
 * 卡片下滑开始
 */
protected void onPullDownStart() {
    if (null != mLoginCardView) {
        mLoginCardView.onPullDownStart();//LoginBaseView
    }
    if (null != mLoginCardView && null != mLoginCardView.loginBasePresenter) {
        mLoginCardView.loginBasePresenter.onPullDownStart();//LoginBasePresenter
    }
}

/**
 * 卡片下滑完成
 */
protected void onPullDownEnd() {
    if (null != mLoginCardView) {
        mLoginCardView.onPullDownEnd();//LoginBaseView
    }
    if (null != mLoginCardView && null != mLoginCardView.loginBasePresenter) {
        mLoginCardView.loginBasePresenter.onPullDownEnd();//LoginBasePresenter
    }
}
View.OnClickListener clickListener = new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        loginInputAccountPresenter.loginBaseFragmentWeakReference.get().showSoftKeyBoard(inputPhoneView.getEditText());
        hideAccountList();
    }
};

inputPhoneView.setOnClickListener(clickListener);
inputPhoneView.getAreaTv().setOnClickListener(clickListener);
inputPhoneView.getEditText().setOnClickListener(clickListener);

焦点联动

子类卡片重写父类卡片中的方法,实现键盘弹起的效果实现焦点联动

@Override
protected void onPullUpEnd() {
    super.onPullUpEnd();
    if ((isClickUpButton || initShowList) && cardView.getVisibility() != View.VISIBLE) {
        //(是否会点击了展示历史账号按钮 || 非切换账号过来) && 没有在展示
        showAccountList(true, false);
        isClickUpButton = false;
        initShowList = false;
    }
}

@Override
protected void onPullDownStart() {
    super.onPullDownStart();
    hideAccountList();
    KeyBoardUtil.closeInputMethod(inputPhoneView.getEditText());//防止键盘没隐藏
}

@Override
protected void onPullDownEnd() {
    super.onPullDownEnd();
    hideAccountList();
    inputPhoneView.showBorder(false);
    inputPhoneView.cleanBtn.setVisibility(View.GONE);
    inputPhoneView.getEditText().setFocusable(false);
}
public class KeyBoardUtil {
    private final static String TAG = "KeyBoardUtil";

    public static boolean isKeyBoardShowing(View view) {
        try {
            InputMethodManager imm = (InputMethodManager) LauncherApplicationAgent.getInstance().
                    getMicroApplicationContext().getApplicationContext().getSystemService(Context.INPUT_METHOD_SERVICE);
            AliUserLog.i(TAG, imm.isActive(view) + "");
            return imm.isActive(view);
        } catch (Exception e) {
            AliUserLog.e(TAG, "closeInputMethod exception" + e.getMessage());
            return false;
        }
    }

    public static boolean isKeyBoardShowing() {
        try {
            InputMethodManager imm = (InputMethodManager) LauncherApplicationAgent.getInstance().
                    getMicroApplicationContext().getApplicationContext().getSystemService(Context.INPUT_METHOD_SERVICE);
            AliUserLog.i(TAG, imm.isActive() + "");
            return imm.isActive();
        } catch (Exception e) {
            AliUserLog.e(TAG, "closeInputMethod exception" + e.getMessage());
            return false;
        }
    }

    /**
     * 关闭软键盘
     *
     * @param view
     */
    public static void closeInputMethod(View view) {
        try {
            InputMethodManager imm = (InputMethodManager) LauncherApplicationAgent.getInstance().
                    getMicroApplicationContext().getApplicationContext().getSystemService(Context.INPUT_METHOD_SERVICE);
            imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
        } catch (Exception e) {
            AliUserLog.e(TAG, "closeInputMethod exception" + e.getMessage());
        }
    }

    /**
     * 打开软键盘
     *
     * @param activity
     * @param view
     * @param delay    延时
     */
    public static void openInputMethodDelay(final Activity activity, final View view, long delay) {
        try {
            activity.getWindow().getDecorView().postDelayed(new Runnable() {
                @Override
                public void run() {
                    openKeyBoardInner(activity, view);
                }
            }, delay);
        } catch (Exception e) {
            AliUserLog.e(TAG, "openInputMethodDelay exception" + e.getMessage());
        }
    }


    /**
     * 打开软键盘
     *
     * @param activity
     * @param view
     */
    public static void openInputMethod(final Activity activity, final View view) {
        try {
            activity.getWindow().getDecorView().post(new Runnable() {
                @Override
                public void run() {
                    openKeyBoardInner(activity, view);
                }
            });
        } catch (Exception e) {
            AliUserLog.e(TAG, "openInputMethod exception" + e.getMessage());
        }
    }

    /**
     * 打开键盘行动类
     *
     * @param activity
     * @param view
     */
    private static void openKeyBoardInner(final Activity activity, final View view) {
        try {
            view.requestFocus();
            if (view instanceof APSafeEditText) {
                APSafeEditText apSafeEditText = (APSafeEditText) view;
                if (apSafeEditText.isPasswordType()) {
                    apSafeEditText.showSafeKeyboard();
                } else {
                    InputMethodManager inputMethodManager = (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE);
                    inputMethodManager.showSoftInput(view, 0);
                }
            } else {
                InputMethodManager inputMethodManager = (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE);
                inputMethodManager.showSoftInput(view, 0);
            }
        } catch (Exception e) {
            AliUserLog.e(TAG, "openKeyBoardInner exception" + e.getMessage());
        }
    }

}

弹窗键盘适配

键盘弹窗遮挡需要根据键盘弹出的高度进行适配

定义弹窗外层布局

public class KeyboardLayout extends FrameLayout {

    private KeyboardLayoutListener mListener;
    private boolean mIsKeyboardActive = false; //输入法是否激活
    private int mKeyboardHeight = 0; // 输入法高度

    public KeyboardLayout(Context context) {
        this(context, null, 0);
    }

    public KeyboardLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public KeyboardLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr); // 监听布局变化
        getViewTreeObserver().addOnGlobalLayoutListener(new KeyboardOnGlobalChangeListener());
    }

    public void setKeyboardListener(KeyboardLayoutListener listener) {
        mListener = listener;
    }

    public KeyboardLayoutListener getKeyboardListener() {
        return mListener;
    }

    public boolean isKeyboardActive() {
        return mIsKeyboardActive;
    }

    /**
     * 获取输入法高度
     *
     * @return
     */
    public int getKeyboardHeight() {
        return mKeyboardHeight;
    }

    public interface KeyboardLayoutListener {
        /**
         * @param isActive       输入法是否激活
         * @param keyboardHeight 输入法面板高度
         */
        void onKeyboardStateChanged(boolean isActive, int keyboardHeight);
    }

    private class KeyboardOnGlobalChangeListener implements ViewTreeObserver.OnGlobalLayoutListener {
        int mScreenHeight = 0;

        private int getScreenHeight() {
            if (mScreenHeight > 0) {
                return mScreenHeight;
            }
            mScreenHeight = ((WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE))
                    .getDefaultDisplay().getHeight();
            return mScreenHeight;
        }

        @Override
        public void onGlobalLayout() {
            Rect rect = new Rect();  // 获取当前页面窗口的显示范围
            ((Activity) getContext()).getWindow().getDecorView().getWindowVisibleDisplayFrame(rect);
            int screenHeight = getScreenHeight();
            int keyboardHeight = screenHeight - rect.bottom; // 输入法的高度
            boolean isActive = false;
            if (Math.abs(keyboardHeight) > screenHeight / 3) {
                isActive = true; // 超过屏幕三分之一则表示弹出了输入法
                mKeyboardHeight = keyboardHeight;
            }
            mIsKeyboardActive = isActive;
            if (mListener != null) {
                mListener.onKeyboardStateChanged(isActive, keyboardHeight);
            }
        }
    }
}

布局文件

<?xml version="1.0" encoding="utf-8"?>
<com.alipay.mobile.verifyidentity.module.dynamic.ui.KeyboardLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    
        <ScrollView
            android:id="@+id/scrollView"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:fillViewport="true"
            android:fitsSystemWindows="true"
            android:scrollbars = "none">

            <com.alipay.mobile.verifyidentity.ui.APRelativeLayout
                android:id="@+id/dynamic_root"
                android:layout_width="match_parent"
                android:layout_height="match_parent" />
        </ScrollView>
</com.alipay.mobile.verifyidentity.module.dynamic.ui.KeyboardLayout>

添加监听和执行滑动

mRootView = findViewById(R.id.dynamic_root);
container = findViewById(R.id.container);
scrollView = findViewById(R.id.scrollView);

/**
 * 监听键盘状态,布局有变化时,靠scrollView去滚动界面
 */
public void addLayoutKeyboardListener() {
    container.setKeyboardListener(new KeyboardLayout.KeyboardLayoutListener() {
        @Override
        public void onKeyboardStateChanged(boolean isActive, int keyboardHeight) {
            VerifyLogCat.i(TAG, "isActive:" + isActive + " keyboardHeight:" + keyboardHeight);
            try {
                scrollToBottom();
            } catch (Exception e) {
                VerifyLogCat.e(TAG, "onKeyboardStateChanged Exception", e);
            }
        }
    });
}

/**
 * 弹出软键盘时将SVContainer滑到底
 */
private void scrollToBottom() {
    if (null == mRootView) {
        return;
    }
    mRootView.postDelayed(new Runnable() {
        @Override
        public void run() {
            try {
                scrollView.smoothScrollTo(0, mRootView.getBottom() / 6);
            } catch (Exception e) {
                VerifyLogCat.e(TAG, "scrollToBottom Exception", e);
            }
        }
    }, 100);
}