自定义安全键盘——仿民生银行

系统自带键盘和第三方的键盘不管是从性能还是从体验上来说都要胜于我们自己写的,但我们为什么还要去自定义键盘呢?其实就为了安全性,比如用户在输入账户密码,支付密码的时候,防止键盘获取到我们的数据;或者说美工要求Android的键盘需要和IOS的一样,那我们就得自己去写个键盘了。
效果图:
keyboardView.gif

一:键盘布局

  • XML布局属性
Keyboard.Key的属性.png
KeyboardView的属性.png
Keyboard.Row的属性.png
ASCII值.png

Key和Row为Keyboard的静态内部类,从上面的图中我们能看到每个属性对应的Description,在这里再简单介绍一下。

Key的xml属性 属性说明
android:codes 表示键Key的标签,字母/符号/数字对应的ASCII值。
android:iconPreview 表示键点击后放大后的View
android:isModifier 按键是否为功能键,例如Alt/Shift/Ctrl键。取值为true或false。
android:isRepeatable 表示长时间按下key键重复执行这个键的操作,如:长按删除键会一直执行删除操作。
android:isSticky 指定按键是否为状态键。如:Shift大小写切换按键,具有两种状态,按下状态和正常状态,取值为true或则false。
android:keyEdgeFlags 指定按键的对齐指令,取值为left或则right。
android:keyIcon 按键图标,如果指定了该值则文本属性无效。
android:keyLabel 代表按键显示的文本内容。
android:keyOutputText 指定按键输出的文本内容,取值为字符串。
android:popupCharacters 表示编辑特殊字符,空格、换行等.。
android:popupKeyboard 表示按键点击预览窗口。
android:horizontalGap 键的水平间隙,当前键的左边的水平间隙。
KeyboardView的xml属性 属性说明
android:background 设置整个键盘的背景色
android:keyPreviewHeight 正在输入时弹出预览框的高度
android:keyPreviewLayout 输入时预览框的布局样式,要求根布局为TextView。
android:keyTextColor 键的字体颜色
android:keyTextSize 键的字体大小
android:labelTextSize 字符串键文本字体大小
android:shadowColor、android:shadowRadius 设置这两个属性可以很好的解决键的字体发虚的问题,设置shadowColor值和键的字体颜色相同。
android:keyBackground 设置键的背景色,可以用drawable中的selector标签设置键的正常状态样式和按下样式
  • 英文键盘
<?xml version="1.0" encoding="UTF-8"?>
<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
          android:horizontalGap="0.0px"
          android:keyHeight="7.5%p"
          android:keyWidth="8%p"
          android:verticalGap="0.0px">

    <Row android:verticalGap="2%p">
        <Key
            android:codes="113"
            android:horizontalGap="1%p"
            android:keyEdgeFlags="left"
            android:keyLabel="q"
            android:keyWidth="8%p"/>
        <Key
            android:codes="119"
            android:horizontalGap="2%p"
            android:keyLabel="w"
            android:keyWidth="8%p"/>
        <Key
            android:codes="101"
            android:horizontalGap="2%p"
            android:keyLabel="e"
            android:keyWidth="8%p"/>
        <Key
            android:codes="114"
            android:horizontalGap="2%p"
            android:keyLabel="r"
            android:keyWidth="8%p"/>
        <Key
            android:codes="116"
            android:horizontalGap="2%p"
            android:keyLabel="t"
            android:keyWidth="8%p"/>
        <Key
            android:codes="121"
            android:horizontalGap="2%p"
            android:keyLabel="y"
            android:keyWidth="8%p"/>
        <Key
            android:codes="117"
            android:horizontalGap="2%p"
            android:keyLabel="u"
            android:keyWidth="8%p"/>
        <Key
            android:codes="105"
            android:horizontalGap="2%p"
            android:keyLabel="i"
            android:keyWidth="8%p"/>
        <Key
            android:codes="111"
            android:horizontalGap="2%p"
            android:keyLabel="o"
            android:keyWidth="8%p"/>
        <Key
            android:codes="112"
            android:horizontalGap="2%p"
            android:keyEdgeFlags="right"
            android:keyLabel="p"
            android:keyWidth="8%p"/>
    </Row>

    <Row android:verticalGap="2%p">
        <Key
            android:codes="97"
            android:horizontalGap="6%p"
            android:keyEdgeFlags="left"
            android:keyLabel="a"
            android:keyWidth="8%p"/>
        <Key
            android:codes="115"
            android:horizontalGap="2%p"
            android:keyLabel="s"
            android:keyWidth="8%p"/>
        <Key
            android:codes="100"
            android:horizontalGap="2%p"
            android:keyLabel="d"
            android:keyWidth="8%p"/>
        <Key
            android:codes="102"
            android:horizontalGap="2%p"
            android:keyLabel="f"
            android:keyWidth="8%p"/>
        <Key
            android:codes="103"
            android:horizontalGap="2%p"
            android:keyLabel="g"
            android:keyWidth="8%p"/>
        <Key
            android:codes="104"
            android:horizontalGap="2%p"
            android:keyLabel="h"
            android:keyWidth="8%p"/>
        <Key
            android:codes="106"
            android:horizontalGap="2%p"
            android:keyLabel="j"
            android:keyWidth="8%p"/>
        <Key
            android:codes="107"
            android:horizontalGap="2%p"
            android:keyLabel="k"
            android:keyWidth="8%p"/>
        <Key
            android:codes="108"
            android:horizontalGap="2%p"
            android:keyEdgeFlags="right"
            android:keyLabel="l"
            android:keyWidth="8%p"/>
    </Row>

    <Row android:verticalGap="2%p">
        <Key
            android:codes="-1"
            android:isModifier="true"
            android:isSticky="true"
            android:horizontalGap="1%p"
            android:keyEdgeFlags="left"
            android:keyIcon="@android:drawable/ic_menu_manage"
            android:keyWidth="12%p"/>
        <Key
            android:codes="122"
            android:horizontalGap="3%p"
            android:keyLabel="z"
            android:keyWidth="8%p"/>
        <Key
            android:codes="120"
            android:horizontalGap="2%p"
            android:keyLabel="x"
            android:keyWidth="8%p"/>
        <Key
            android:codes="99"
            android:horizontalGap="2%p"
            android:keyLabel="c"
            android:keyWidth="8%p"/>
        <Key
            android:codes="118"
            android:horizontalGap="2%p"
            android:keyLabel="v"
            android:keyWidth="8%p"/>
        <Key
            android:codes="98"
            android:horizontalGap="2%p"
            android:keyLabel="b"
            android:keyWidth="8%p"/>
        <Key
            android:codes="110"
            android:horizontalGap="2%p"
            android:keyLabel="n"
            android:keyWidth="8%p"/>
        <Key
            android:codes="109"
            android:horizontalGap="2%p"
            android:keyLabel="m"
            android:keyWidth="8%p"/>
        <Key
            android:codes="-5"
            android:horizontalGap="3%p"
            android:isRepeatable="true"
            android:keyEdgeFlags="right"
            android:keyIcon="@drawable/img_edit_clear"
            android:keyWidth="12%p"/>
    </Row>

    <Row>
        <Key
            android:codes="-2"
            android:isModifier="true"
            android:isSticky="true"
            android:horizontalGap="1%p"
            android:keyEdgeFlags="left"
            android:keyLabel="123"
            android:keyWidth="20%p"/>
        <Key
            android:codes="32"
            android:horizontalGap="5%p"
            android:isRepeatable="true"
            android:keyLabel="space"
            android:keyWidth="48%p"/>
        <Key
            android:codes="-4"
            android:horizontalGap="5%p"
            android:keyEdgeFlags="right"
            android:keyLabel="完成"
            android:keyWidth="20%p"/>
    </Row>

</Keyboard>
  • 数字键盘
<?xml version="1.0" encoding="UTF-8"?>
<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
          android:horizontalGap="0px"
          android:keyHeight="7.5%p"
          android:keyWidth="30%p"
          android:verticalGap="0px">

    <Row android:verticalGap="2%p">
        <Key
            android:codes="49"
            android:horizontalGap="2%p"
            android:keyEdgeFlags="left"
            android:keyLabel="1"
            android:keyWidth="30%p"/>
        <Key
            android:codes="50"
            android:horizontalGap="3%p"
            android:keyLabel="2"
            android:keyWidth="30%p"/>
        <Key
            android:codes="51"
            android:horizontalGap="3%p"
            android:keyEdgeFlags="right"
            android:keyLabel="3"
            android:keyWidth="30%p"/>
    </Row>

    <Row android:verticalGap="2%p">
        <Key
            android:codes="52"
            android:horizontalGap="2%p"
            android:keyLabel="4"
            android:keyWidth="30%p"/>
        <Key
            android:codes="53"
            android:horizontalGap="3%p"
            android:keyLabel="5"
            android:keyWidth="30%p"/>
        <Key
            android:codes="54"
            android:horizontalGap="3%p"
            android:keyEdgeFlags="right"
            android:keyLabel="6"
            android:keyWidth="30%p"/>
    </Row>

    <Row android:verticalGap="2%p">
        <Key
            android:codes="55"
            android:horizontalGap="2%p"
            android:keyLabel="7"
            android:keyWidth="30%p"/>
        <Key
            android:codes="56"
            android:horizontalGap="3%p"
            android:keyLabel="8"
            android:keyWidth="30%p"/>
        <Key
            android:codes="57"
            android:horizontalGap="3%p"
            android:keyEdgeFlags="right"
            android:keyLabel="9"
            android:keyWidth="30%p"/>
    </Row>

    <Row>
        <Key
            android:codes="-2"
            android:horizontalGap="2%p"
            android:isModifier="true"
            android:isRepeatable="false"
            android:isSticky="true"
            android:keyEdgeFlags="left"
            android:keyLabel="abc"
            android:keyWidth="30%p"/>
        <Key
            android:codes="48"
            android:horizontalGap="3%p"
            android:keyLabel="0"
            android:keyWidth="30%p"/>
        <Key
            android:codes="-5"
            android:horizontalGap="3%p"
            android:isRepeatable="true"
            android:keyEdgeFlags="right"
            android:keyIcon="@drawable/img_edit_clear"
            android:keyWidth="30%p"/>
    </Row>

</Keyboard> 

在键盘布局的时候,要保证键所占的宽度(包括间隙)所占的比例和为100%,不然整个键盘的右边会过宽或过窄。如:数字键盘的第一行:2+30+3+30+3+30+2=100,第一个2和最后一个2是键盘离屏幕的距离。

二:代码实现

  • 自定义键的按下效果
    我们有时候可能需要为删除键设置这样的按下效果,为退格键设置那样的效果;设置键的按下效果可通过KeyboardView的android:keyBackground属性进行设置,但是所有键的按下效果都是一样的,不能满足我们的需求;但是我们可以继承系统KeyboardView,在onDraw方法中通过键的code(ASCII值)为不同的键绘制不同的背景、图标、文本。
public class SKeyboardView extends KeyboardView {
    private Context context;
    private Rect rect;
    private Paint paint;

    private int keyboardType = -1;

    public SKeyboardView(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.context = context;
        initSKeyboardView();
    }

    public SKeyboardView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.context = context;
        initSKeyboardView();
    }

    /**
     * 初始化画笔等
     */
    private void initSKeyboardView() {
        rect = new Rect();

        paint = new Paint();
        paint.setTextSize(70);
        paint.setAntiAlias(true);
        paint.setColor(Color.BLACK);
        paint.setTextAlign(Paint.Align.CENTER);
    }

    @Override
    public void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (getKeyboard() == null) {
            return;
        }
        List<Keyboard.Key> keys = getKeyboard().getKeys();

        if (keyboardType == 0) {// 数字键盘
            drawKeyboardNumber(keys, canvas);
        } else if (keyboardType == 1) {// 英文键盘
            drawKeyboardEnglish(keys, canvas);
        }
    }

    /**
     * 绘制数字键盘
     *
     * @param keys
     * @param canvas
     */
    private void drawKeyboardNumber(List<Keyboard.Key> keys, Canvas canvas) {
        for (Keyboard.Key key : keys) {
            if (key.codes[0] == -5) {//删除键
                drawKeyBackground(R.drawable.img_edit_clear, canvas, key);
            }
        }
    }

    /**
     * 绘制英文键盘
     *
     * @param keys
     * @param canvas
     */
    private void drawKeyboardEnglish(List<Keyboard.Key> keys, Canvas canvas) {
        for (Keyboard.Key key : keys) {
            if (key.codes[0] == -5) {//删除键
                drawKeyBackground(R.drawable.img_edit_clear, canvas, key);
            }
            if (key.codes[0] == -1) {//大小写切换
                drawKeyBackground(R.drawable.img_edit_clear, canvas, key);
            }
            if (key.codes[0] == 32) {//space

            }
            if (key.codes[0] == -4) {//完成

            }
        }
    }

    /**
     * 设置当前键盘标识 0:数字键盘;1:英文键盘
     *
     * @param keyboardType
     */
    public void setCurrentKeyboard(int keyboardType) {
        this.keyboardType = keyboardType;
        invalidate();
    }

    /**
     * 绘制键盘key的背景
     *
     * @param drawableId 将要绘制上去的图标
     * @param canvas
     * @param key        需要绘制的键
     */
    private void drawKeyBackground(int drawableId, Canvas canvas, Keyboard.Key key) {
        Drawable npd = ResUtil.getDrawable(drawableId);
        int[] drawableState = key.getCurrentDrawableState();
        if (key.codes[0] != 0) {
            npd.setState(drawableState);
        }
        npd.setBounds(key.x, key.y, key.x + key.width, key.y + key.height);
        npd.draw(canvas);
    }

    /**
     * 绘制字体
     *
     * @param canvas
     * @param key
     */
    private void drawKeyText(Canvas canvas, Keyboard.Key key) {
        if (keyboardType == 0) {
            if (key.label != null) {
                paint.getTextBounds(key.label.toString(), 0, key.label.toString().length(), rect);
                canvas.drawText(key.label.toString(), key.x + (key.width / 2), (key.y + key.height / 2) + rect.height() / 2, paint);
            }
        } else if (keyboardType == 1) {
            if (key.label != null) {
                paint.getTextBounds(key.label.toString(), 0, key.label.toString().length(), rect);
                canvas.drawText(key.label.toString(), key.x + (key.width / 2), (key.y + key.height / 2) + rect.height() / 2, paint);
            }
        }
    }

    @Override
    public void onDetachedFromWindow() {
        super.onDetachedFromWindow();
    }
}
  • 输入的控制逻辑处理
    由于在应用中只有在输入安全性要求较高的地方才会用到我们自定义的键盘,所以我就继承EditText,在子类中写相关控制逻辑,避免在使用的时候写更多代码。
public class EditView extends EditText implements SKeyboardView.OnKeyboardActionListener {
    private Context context;

    private Keyboard keyboardNumber;
    private Keyboard keyboardEnglish;
    private ViewGroup viewGroup;
    private SKeyboardView keyboardView;

    //标识数字键盘和英文键盘的切换
    private boolean isShift = true;
    //标识英文键盘大小写切换
    private boolean isCapital = false;

    //点击【完成】、键盘隐藏、键盘显示时的回调
    private OnKeyboardListener onKeyboardListener;

    public EditView(Context context) {
        this(context, null);
    }

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

    public EditView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.context = context;
        initEditView();
    }

    /**
     * 初始化自定义键盘
     */
    private void initEditView() {
        keyboardNumber = new Keyboard(context, R.xml.keyboard_number);
        keyboardEnglish = new Keyboard(context, R.xml.keyboard_english);
    }

    /**
     * 设置键盘
     *
     * @param viewGroup
     * @param keyboardView
     * @param isNumber     true:表示默认数字键盘,false:表示默认英文键盘
     */
    public void setEditView(ViewGroup viewGroup, SKeyboardView keyboardView, boolean isNumber) {
        this.viewGroup = viewGroup;
        this.keyboardView = keyboardView;
        this.isShift = isNumber;

        if (isNumber) {
            keyboardView.setKeyboard(keyboardNumber);
            keyboardView.setCurrentKeyboard(0);
        } else {
            keyboardView.setKeyboard(keyboardEnglish);
            keyboardView.setCurrentKeyboard(1);
        }
        keyboardView.setEnabled(true);
        keyboardView.setPreviewEnabled(!isNumber);
        keyboardView.setOnKeyboardActionListener(this);
    }

    @Override
    public void onAttachedToWindow() {
        super.onAttachedToWindow();
        SystemUtil.closeKeyboard(this);
    }

    @Override
    public void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        SystemUtil.closeKeyboard(this);
        keyboardView = null;
        viewGroup = null;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        super.onTouchEvent(event);
        requestFocus();
        requestFocusFromTouch();
        SystemUtil.closeKeyboard(this);
        if (event.getAction() == MotionEvent.ACTION_UP) {
            if (!isShow()) {
                show();
            }
        }
        return true;
    }

    @Override
    public void onPress(int primaryCode) {
        if (onKeyboardListener != null) {
            onKeyboardListener.onPress(primaryCode);
        }
        if (isShift) {
            return;
        }
        setPreview(primaryCode);
    }

    @Override
    public void onRelease(int primaryCode) {
        switch (primaryCode) {
            case Keyboard.KEYCODE_DONE:// 完成-4
                hide(true);
                break;

            default:
                break;
        }
    }

    @Override
    public void onKey(int primaryCode, int[] ints) {
        Editable editable = getText();
        int start = getSelectionStart();

        switch (primaryCode) {
            case Keyboard.KEYCODE_MODE_CHANGE:// 英文键盘与数字键盘切换-2
                shiftKeyboard();
                break;
            case Keyboard.KEYCODE_DELETE:// 回退-5
                if (editable != null && editable.length() > 0 && start > 0) {
                    editable.delete(start - 1, start);
                }
                break;
            case Keyboard.KEYCODE_SHIFT:// 英文大小写切换-1
                shiftEnglish();
                keyboardView.setKeyboard(keyboardEnglish);
                break;
            case Keyboard.KEYCODE_DONE:// 完成-4
                break;

            default:
                editable.insert(start, Character.toString((char) primaryCode));
                break;
        }
    }

    /**
     * 切换键盘
     */
    private void shiftKeyboard() {
        if (isShift) {
            keyboardView.setKeyboard(keyboardEnglish);
            keyboardView.setCurrentKeyboard(1);
        } else {
            keyboardView.setKeyboard(keyboardNumber);
            keyboardView.setCurrentKeyboard(0);
        }
        isShift = !isShift;
    }

    /**
     * 英文键盘大小写切换
     */
    private void shiftEnglish() {
        List<Keyboard.Key> keyList = keyboardEnglish.getKeys();
        for (Keyboard.Key key : keyList) {
            if (key.label != null && isKey(key.label.toString())) {
                if (isCapital) {
                    key.label = key.label.toString().toLowerCase();
                    key.codes[0] = key.codes[0] + 32;
                } else {
                    key.label = key.label.toString().toUpperCase();
                    key.codes[0] = key.codes[0] - 32;
                }
            }
        }
        isCapital = !isCapital;
    }

    /**
     * 判断是否需要预览Key
     *
     * @param primaryCode keyCode
     */
    private void setPreview(int primaryCode) {
        List<Integer> list = Arrays.asList(Keyboard.KEYCODE_MODE_CHANGE, Keyboard.KEYCODE_DELETE, Keyboard.KEYCODE_SHIFT, Keyboard.KEYCODE_DONE, 32);
        if (list.contains(primaryCode)) {
            keyboardView.setPreviewEnabled(false);
        } else {
            keyboardView.setPreviewEnabled(true);
        }
    }

    /**
     * 判断此key是否正确,且存在
     *
     * @param key
     * @return
     */
    private boolean isKey(String key) {
        String lowercase = "abcdefghijklmnopqrstuvwxyz";
        if (lowercase.indexOf(key.toLowerCase()) > -1) {
            return true;
        }
        return false;
    }

    /**
     * 设置键盘隐藏
     *
     * @param isCompleted true:表示点击了【完成】
     */
    public void hide(boolean isCompleted) {
        int visibility = keyboardView.getVisibility();
        if (visibility == View.VISIBLE) {
            keyboardView.setVisibility(View.INVISIBLE);
            if (viewGroup != null) {
                viewGroup.setVisibility(View.GONE);
            }
        }
        if (onKeyboardListener != null) {
            onKeyboardListener.onHide(isCompleted);
        }
    }

    /**
     * 设置键盘对话框显示,并且屏幕上移
     */
    public void show() {
        //设置键盘显示
        int visibility = keyboardView.getVisibility();
        if (visibility == View.GONE || visibility == View.INVISIBLE) {
            keyboardView.setVisibility(View.VISIBLE);
            if (viewGroup != null) {
                viewGroup.setVisibility(View.VISIBLE);
            }
        }
        if (onKeyboardListener != null) {
            onKeyboardListener.onShow();
        }
    }

    /**
     * 键盘状态
     *
     * @return true:表示键盘开启 false:表示键盘隐藏
     */
    public boolean isShow() {
        return keyboardView.getVisibility() == View.VISIBLE;
    }

    @Override
    public void onText(CharSequence charSequence) {

    }

    @Override
    public void swipeLeft() {

    }

    @Override
    public void swipeRight() {

    }

    @Override
    public void swipeDown() {

    }

    @Override
    public void swipeUp() {

    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK) {
            hide(false);
            return true;
        }
        return super.onKeyDown(keyCode, event);
    }

    public interface OnKeyboardListener {
        /**
         * 键盘隐藏了
         *
         * @param isCompleted true:表示点击了【完成】
         */
        void onHide(boolean isCompleted);

        /**
         * 键盘弹出了
         */
        void onShow();

        /**
         * 按下
         *
         * @param primaryCode
         */
        void onPress(int primaryCode);
    }

    /**
     * 对外开放的方法
     *
     * @param onKeyboardListener
     */
    public void setOnKeyboardListener(OnKeyboardListener onKeyboardListener) {
        this.onKeyboardListener = onKeyboardListener;
    }
}

相同英文字母大小写的ASCII值相差32;在键盘切换的时候,如果当前为小写键盘样式,要切换为大写,则把对应字母的ASCII值减去32,并且键上显示的字母设置为大写字母。

三:开发中遇到的问题

  • 输入预览
    设置预览框大小的时候,设置android:layout_width、android:layout_height属性是无效的,因为预览框是弹出的PopupWindow,所以我们只有设置TextView的android:paddingLeft、android:paddingRight属性。在此也可以设置预览框的背景色,字体颜色等等。
  • 设置不同键的按下状态的样式
    想要给不同的键设置不同的按下效果的时候,只能设置统一的样式,不能满足我们的需求,这时继承系统KeyboardView,并在onDraw中绘制不同键的背景可以解决问题。
  • 把自定义键盘放入PopupWindow中,开启输入预览时崩溃
    ERROR/AndroidRuntime(888): android.view.WindowManager$BadTokenException: Unable to add window -- token android.view.ViewRoot$W@44ef1b68 is not valid; is your activity running?
    PopupWindow中再弹出PopupWindow,后面的PopupWindow没有载体了,已经不在视图窗口中了。
  • 软键盘遮挡编辑框
    不同场景解决方案方案,在此不作说明。
  • 光标消失?
    在使用的过程中有时候,光标会消失,若有好的解决方案可以告知下,Thanks!

四:具体使用

editView.setEditView(llKeyboard, keyboardView, true);

editView.setOnKeyboardListener(new EditView.OnKeyboardListener() {
    @Override
    public void onHide(boolean isCompleted) {
        if (height > 0) {
            llGuan.scrollBy(0, -(height + DensityUtil.dp2px(MainActivity.this, 16)));
        }

        if (isCompleted) {
            Log.i("", "你点击了完成按钮");
        }
    }

    @Override
    public void onShow() {
        llGuan.post(new Runnable() {
            @Override
            public void run() {
                //pos[0]: X,pos[1]: Y
                int[] pos = new int[2];
                //获取编辑框在整个屏幕中的坐标
                editView.getLocationOnScreen(pos);
                //编辑框的Bottom坐标和键盘Top坐标的差
                height = (pos[1] + editView.getHeight())
                        - (ScreenUtil.getScreenHeight(MainActivity.this) - keyboardView.getHeight());
                if (height > 0) {
                    //编辑框和键盘之间预留出16dp的距离
                    llGuan.scrollBy(0, height + DensityUtil.dp2px(MainActivity.this, 16));
                }
            }
        });
    }

    @Override
    public void onPress(int primaryCode) {

    }
});

llKeyboard为包裹键盘的父布局,llGuan为包裹输入框的父布局。

源码:https://github.com/GitPhoenix/KeyboardView

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,542评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,596评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,021评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,682评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,792评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,985评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,107评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,845评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,299评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,612评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,747评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,441评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,072评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,828评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,069评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,545评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,658评论 2 350

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,846评论 25 707
  • ¥开启¥ 【iAPP实现进入界面执行逐一显】 〖2017-08-25 15:22:14〗 《//首先开一个线程,因...
    小菜c阅读 6,373评论 0 17
  • 内容抽屉菜单ListViewWebViewSwitchButton按钮点赞按钮进度条TabLayout图标下拉刷新...
    皇小弟阅读 46,734评论 22 665
  • 今天听银银同学和孩子们聊,怎么才能把自己的特点(特别是在大家眼里不太好的特点)变成优点? 我身边一个小...
    陈蕾FZ阅读 695评论 0 50
  • 上完自习便往宿舍走,中途经过了操场。在月光的映衬下可以看到操场上有许多的人影。有的两两牵手散步的情侣,有嬉戏打闹女...
    桥上的人阅读 278评论 0 0