对于触屏手机软键盘是正常使用中不可或缺的一样东西,在Android编程时经常会遇到一些软键盘方面的文字,经过本人实践总结,把有关的坑写在下面。

Android为了让用户在软键盘弹出后能显示出文本编辑框(也就是EditView),会默认判断如果键盘弹出后会遮挡编辑框便让界面布局挤压,将编辑框强行顶在软键盘上方,这样对于用户体验来说是挺好的,但是对于一些复杂的界面,经常会出现很多问题。这里我们便需要人工判断系统软键盘应该如何显示。

可以通过修改Manifest中对应Activity的属性android:windowSoftInputMode进行修改。

关于这个属于网上有很多相关的介绍,但是看了后总觉得很怪,像是硬生生从文档中字译过来的,这里结合我的感受简单说下几个的区别:

该属性中分为两类,一类为adjust,用于控制布局受键盘影响的情况。另一类为state,用于控制键盘的情况。

adjust中有:adjustNothing,无论什么情况,键盘都不会挤压布局

adjustPan,键盘会挤压布局并且将编辑框类控件顶上去

adjustUnspecified,键盘不会挤压布局但是会将编辑框类控件顶上去

adjustResize,暂时没试过。


开发中我们还经常会遇到需要获得系统软键盘高度的情况,但是Android不像Ios是没有向我们提供有关参数的接口的,通过网上查询,发现大多是通过监听全局视图树的方法来得到软键盘的高度,大致原理是监听视图变化,当键盘弹出后可见视图变小,通过屏幕高度来减去可见高度,最后得到键盘高度。

网上代码各式各样,而且写的也比较繁琐,不利于使用,于是我自己将其封装成了一个方法方便其调用。


/**
     * 获得软键盘高度
     */
    public interface KeyboardListener {
        void onKeyboardChange(int keyboardHeight);
    }

    public static void getKeyboardHeight(final Activity activity, final KeyboardListener keyboardListener) {
        final View parentLayout = activity.getWindow().getDecorView();
        parentLayout.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                Rect r = new Rect();
                // r will be populated with the coordinates of your view that area still visible.
                parentLayout.getWindowVisibleDisplayFrame(r);
                int screenHeight = MyApplication.windowHeight;
                int heightDiff = screenHeight - (r.bottom - r.top);
//                if (heightDiff > 100)
                // if more than 100 pixels, its probably a keyboard
                // get status bar height
                int statusBarHeight = 0;
                try {
                    Class<?> c = Class.forName("com.android.internal.R$dimen");
                    Object obj = c.newInstance();
                    Field field = c.getField("status_bar_height");
                    int x = Integer.parseInt(field.get(obj).toString());
                    statusBarHeight = activity.getResources().getDimensionPixelSize(x);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                int realKeyboardHeight = heightDiff - statusBarHeight;
                keyboardListener.onKeyboardChange(realKeyboardHeight);
            }
        });
    }

通过在activity中进行注册,并且重写回调,便能在软键盘出现后拿到对应的高度。

这里又带来了一个新的问题,拿到软键盘高度那也得是在软键盘出现过之后,并且经过测试很多场景想在第一次拿到软键盘高度就拿来使用是不现实的,因为该回调会在软键盘完全出现之后(可能还有一定延迟)才会触发回调。因此我是通过在首页面或者是某个特定的页面,手动让键盘先弹出一次,然后在自动隐藏来获得键盘高度。

以下是弹出方法

/**
     * 得到软键盘高度
     */
    private void initKeyboard() {
        if (SpUtil.getInstance().getBoardHeight() > 0) {
            return;
        }
        PublicUtils.getKeyboardHeight(this, this);
        new Thread(new Runnable() {
            InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        imm.toggleSoftInput(0, InputMethodManager.RESULT_HIDDEN);
                    }
                });
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        imm.toggleSoftInput(InputMethodManager.RESULT_SHOWN, 0);
                    }
                });
            }
        }).start();
    }


之所有要在子线程是因为弹出键盘需要一个延迟,如果没有延迟放在onCreate中可能会在布局没有准备好的情况下进行调用,这时是无法弹出键盘的,当然也能通过监听布局加载完成来调用,但是没有这个必要(本人比较懒),还有一点就是在键盘弹出之后同样需要添加一个延迟,否则直接关闭会导致键盘还没有弹出就被关闭,准确说是执行了弹出操作但是视图还没来得及发生改变就关闭,而我们的方法是通过监听视图变化,所以这样无法拿到键盘高度。

如果有什么说的不对或者不详细的地方,欢迎大家补充。