Android自定义键盘:数字键盘和字母键盘

3,850 阅读3分钟

在项目中,产品对于输入方式会有特殊的要求,需要对输入方式增加特定的限制,这就需要采用自定义键盘。本文主要讲述数字键盘和字母键盘的自定义实现。

项目地址:github.com/xudjx/djkey…
键盘效果:


自定义键盘的实现步骤如下:

  1. 自定义CustomKeyboard, 继承自系统Keyboard,实现KeyboardView.OnKeyboardActionListener相关接口,以处理用户的点击回调;
  2. 自定义CustomKeyboardView, 继承自KeyboardView,实现自定义键盘绘制;
  3. 创建KeyboardManager, 用于处理自定义键盘的显示以及和输入UI的交互

自定义CustomKeyboard

Android系统Keyboard的构造方法如下:

 /**
     * Creates a keyboard from the given xml key layout file.
     * @param context the application or service context
     * @param xmlLayoutResId the resource file that contains the keyboard layout and keys.
     */
    public Keyboard(Context context, int xmlLayoutResId) {
        this(context, xmlLayoutResId, 0);
    }

    /**
     * Creates a keyboard from the given xml key layout file. Weeds out rows
     * that have a keyboard mode defined but don't match the specified mode.
     * @param context the application or service context
     * @param xmlLayoutResId the resource file that contains the keyboard layout and keys.
     * @param modeId keyboard mode identifier
     * @param width sets width of keyboard
     * @param height sets height of keyboard
     */
    public Keyboard(Context context, @XmlRes int xmlLayoutResId, int modeId, int width,
            int height) {
            ...
    }
    等等

其中,参数xmlLayoutResId是必须的,另外还可以通过计算系统键盘的高度来设定自定义键盘的高度。 xmlLayoutRes的格式如下:

<?xml version="1.0" encoding="UTF-8"?>
<Keyboard android:keyWidth="24.9%p"
          android:keyHeight="49dp"
          android:horizontalGap="0.1333%p"
          android:verticalGap="1px"
          xmlns:android="http://schemas.android.com/apk/res/android">
    <Row>
        <Key android:codes="49" android:keyEdgeFlags="left"
             android:keyLabel="1" />
        <Key android:codes="50" android:keyLabel="2" />
        <Key android:codes="51" android:keyLabel="3" />
        <Key android:codes="-5" android:iconPreview="@drawable/key_num_del_bg"
             android:isRepeatable="true"/>
    </Row>
    ...
</Keyboard>

详细的数字键盘和字母键盘xmlLayoutRes资源文件可以从以下链接获取:
数字键盘xmlLayoutRes
字母键盘xmlLayoutRes

CustomKeyboard主要目的就是赋予xmlLayoutRes并实现特定按键的点击处理,其主要重载的方法是onKey(int primaryCode, int[] keyCodes)。详细代码如下:

public abstract class BaseKeyboard extends Keyboard implements KeyboardView.OnKeyboardActionListener{

  @Override
  public void onKey(int primaryCode, int[] keyCodes) {
        if(null != mEditText && mEditText.hasFocus() && !handleSpecialKey(primaryCode)) {
            Editable editable = mEditText.getText();
            int start = mEditText.getSelectionStart();
            int end = mEditText.getSelectionEnd();
            if (end > start){
                editable.delete(start,end);
            }
            if(primaryCode == KEYCODE_DELETE) {
                if(!TextUtils.isEmpty(editable)) {
                    if(start > 0) {
                        editable.delete(start-1,start);
                    }
                }
            }else if(primaryCode == getKeyCode(R.integer.hide_keyboard)){
                hideKeyboard();
            }else {
                editable.insert(start,Character.toString((char) primaryCode));
            }
        }
    }

  public abstract boolean handleSpecialKey(int primaryCode);
}

如上所示是BaseKeyboard,数字键盘和字母键盘需要继承它,并实现public abstract boolean handleSpecialKey(int primaryCode)方法。

自定义CustomKeyboardView

KeyboardView 是承载不同的keyboard并绘制keyboard, 是键盘布局的绘制板, 并与系统交互。通过继承KeyboardView自定义CustomKeyboardView,可以对按键样式实现自定义。考察KeyboardView的源码,发现其UI样式都是private类型,这就需要通过反射的方式获取特定的UI属性,并重新进行赋值,同时重载onDraw()方法,在onDraw()中重新绘制。

详细代码可以参考github源码: BaseKeyBoardView源码

自定义键盘的UI效果如下:

数字键盘
数字键盘
字母键盘
字母键盘

创建KeyboardManager

主要处理以下功能逻辑:

  1. 绑定EditText和Keyboard,监听EditText的OnFocusChangeListener,处理键盘弹出和键盘掩藏;
  2. 处理系统键盘和自定义键盘之间的切换关系;
  3. 处理键盘区域其他自定义view的显示,比如需要让键盘自动搜索功能时,可在manager中进行相关处理

以绑定EditText为例:

public void bindToEditor(EditText editText, BaseKeyboard keyboard) {
    hideSystemSoftKeyboard(editText);
    editText.setTag(R.id.bind_keyboard_2_editor, keyboard);
    if (keyboard.getKeyStyle() == null) {
        keyboard.setKeyStyle(mDefaultKeyStyle);
    }
    editText.setOnFocusChangeListener(editorFocusChangeListener);
}

private final View.OnFocusChangeListener editorFocusChangeListener = new View.OnFocusChangeListener() {
    @Override
    public void onFocusChange(final View v, boolean hasFocus) {
        if (v instanceof EditText) {
            if (hasFocus) {
              v.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        showSoftKeyboard((EditText) v);
                    }
                },300);
            } else {
                hideSoftKeyboard();
            }
        }
    }
};

public void showSoftKeyboard(EditText editText) {
    mRootView.addOnLayoutChangeListener(mOnLayoutChangeListener);
    BaseKeyboard keyboard = getBindKeyboard(editText);
    if (keyboard == null) {
        Log.e(TAG, "edit text not bind to keyboard");
        return;
    }
    keyboard.setEditText(editText);
    keyboard.setNextFocusView(mKeyboardWithSearchView.getEditText());
    initKeyboard(keyboard);
    ...
}

键盘的使用方式非常简单, 通过KeyboardManager实现调用

数字键盘:

KeyboardManager keyboardManagerNumber = new KeyboardManager(this);
keyboardManagerNumber.bindToEditor(editText2, new NumberKeyboard(context,NumberKeyboard.DEFAULT_NUMBER_XML_LAYOUT));

字母键盘:

KeyboardManager keyboardManagerAbc = new KeyboardManager(this);
keyboardManagerAbc.bindToEditor(editText1, new ABCKeyboard(context, ABCKeyboard.DEFAULT_ABC_XML_LAYOUT));

至此,自定义键盘的实现就介绍完了,文中介绍的更多还是实现的思路,具体实现可以参考github,有需要的用户也可以直接修改项目的源码。