前段时间改造了公司的安全键盘,是基于Dialog
和Button
自定义的。也因此借机了解下 Android 平台提供的自定义键盘接口。主要有两个类:Keyboard
和KeyboardView
。很搞笑的是,百度出来自定义Android键盘(与自定义Android输入法不同)的文章千篇一律。
注:这里讲的自定义键盘同公司安全键盘是两种实现方式,不存在泄露公司内部技术的问题!!!
不去吐槽别人,楼主秉持只原创和翻译的作风,输出这第50篇博客。相关属性部分是对照官方文档和Demo实践翻译的,若有瑕疵,请见谅。楼主csdn主页请点击flueky专栏。
1 相关属性
1.1 Keyboard
序号 |
属性 |
类型 |
描述 |
1 |
keyHeight |
dimension/fractional |
Key高度,区分精确值(dp、px等)和相对值(%、%p) |
2 |
keyWidth |
dimension/fractional |
Key宽度,同上 |
3 |
horizontalGap |
dimension/fractional |
Key水平间隙,同上 |
4 |
verticalGap |
dimension/fractional |
Key按键间隙(垂直),同上 |
1.2 Row
序号 |
属性 |
类型 |
描述 |
1 |
keyHeight |
dimension/fractional |
Key高度,区分精确值(dp、px等)和相对值(%、%p) |
2 |
keyWidth |
dimension/fractional |
Key宽度,同上 |
3 |
horizontalGap |
dimension/fractional |
Key水平间隙,同上 |
4 |
verticalGap |
dimension/fractional |
Key按键间隙(垂直),同上 |
5 |
keyboardMode |
reference |
键盘类型,如果该行的类型不符合键盘的类型,将跳过该行。 |
6 |
rowEdgeFlags |
enum |
行边界标记,top/bottom,键盘顶(底)部锚点。 |
1.3 Key
序号 |
属性 |
类型 |
描述 |
1 |
keyHeight |
dimension/fractional |
Key高度,区分精确值(dp、px等)和相对值(%、%p) |
2 |
keyWidth |
dimension/fractional |
Key宽度,同上 |
3 |
horizontalGap |
dimension/fractional |
Key水平间隙,同上 |
4 |
verticalGap |
dimension/fractional |
Key按键间隙(垂直),同上 |
5 |
codes |
int |
Key输出符号对应的Unicode值,官方还说支持字转义字符串,不明白。 |
6 |
iconPreview |
reference |
弹出回显的icon |
7 |
isModifier |
boolean |
是否功能修饰键,如:Alt/Shift |
8 |
isSticky |
boolean |
是否是开关键 |
9 |
isRepeatable |
boolean |
是否允许重复。true表示长按时重复执行。 |
10 |
keyEdgeFlags |
enum |
Key边缘位置标记,left/right,键盘左(右)边锚点。 |
11 |
keyIcon |
reference |
替换label显示在按键上的icon。 |
12 |
keyLabel |
reference |
显示在Key上的标签。 |
13 |
keyOutputText |
string |
Key按下时输出的字符或字符串。 |
14 |
popupCharacters |
string |
小键盘显示的字符,用于显示Key候选项。 |
15 |
popupKeyboard |
reference |
按键候选小键盘的keyboard布局 |
1.4 KeyboardView
序号 |
属性 |
类型 |
描述 |
1 |
keyBackground |
reference |
按键的图像背景,必须包含多个状态的drawable |
2 |
verticalCorrection |
dimension |
补充触摸y坐标的偏移,用于偏差矫正 |
3 |
keyPreviewLayout |
reference |
按键按下时预览框的布局 |
4 |
keyPreviewOffset |
dimension |
按键按下时预览框的偏移。>0 向下,<0 向上。 |
5 |
keyPreviewHeight |
dimension |
按键按下时预览框的高度。 |
6 |
keyTextSize |
dimension |
按键文字大小。 |
7 |
keyTextColor |
color |
按键文字颜色。 |
8 |
labelTextSize |
dimension |
标签文字大小,keylabel有多个字符且keycodes只有一个值时,该属性生效。 |
9 |
popupLayout |
reference |
按键候选小键盘的KeyboardView布局。 |
10 |
shadowRadius |
float |
按键文字阴影半径 |
11 |
shadowColor |
color |
按键文字阴影颜色 |
2 自定义键盘
2.1 布局
1 2 3 4 5 6 7 8 9 10 11
| <android.inputmethodservice.KeyboardView android:id="@+id/activity_main_keyboard" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="#212121" android:keyBackground="@drawable/key_bg" android:keyTextColor="#dddddd" android:keyTextSize="18sp" android:labelTextSize="18sp" android:paddingBottom="2dp" android:paddingTop="2dp" />
|
键盘容器视图,Demo中直接放在Activity布局。KeyboardView可以显示不同类型的Keyboard。请区分background
和keyBackground
。keyBackground
内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| <?xml version="1.0" encoding="utf-8"?> <layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <item android:bottom="2dp" android:left="2dp" android:right="2dp" android:top="2dp"> <selector> <item android:state_pressed="true"> <shape> <solid android:color="#565656" /> <corners android:radius="5dp" /> </shape> </item> <item> <shape> <solid android:color="#383838" /> <corners android:radius="5dp" /> </shape> </item> </selector> </item> </layer-list>
|
2.1.1 字母键盘布局
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
| <?xml version="1.0" encoding="utf-8"?> <Keyboard xmlns:android="http://schemas.android.com/apk/res/android" android:keyHeight="50dp" android:keyWidth="10%p"> <Row android:rowEdgeFlags="top"> <Key android:keyEdgeFlags="left" android:keyLabel="q" /> <Key android:keyLabel="w" /> <Key android:keyLabel="e" /> <Key android:keyLabel="r" /> <Key android:keyLabel="t" /> <Key android:keyLabel="y" /> <Key android:keyLabel="u" /> <Key android:keyLabel="i" /> <Key android:keyLabel="o" /> <Key android:keyEdgeFlags="right" android:keyLabel="p" /> </Row> <Row> <Key android:codes="97" android:horizontalGap="5%p" android:keyEdgeFlags="left" android:keyLabel="a" /> <Key android:keyLabel="s" /> <Key android:keyLabel="d" /> <Key android:keyLabel="f" /> <Key android:keyLabel="g" /> <Key android:keyLabel="h" /> <Key android:keyLabel="j" /> <Key android:keyLabel="k" /> <Key android:keyEdgeFlags="right" android:keyLabel="l" /> </Row> <Row> <Key android:codes="-1" android:isModifier="true" android:isSticky="true" android:keyEdgeFlags="left" android:keyIcon="@drawable/key_caps_lock_icon" android:keyWidth="15%p" /> <Key android:keyLabel="z" /> <Key android:keyLabel="x" /> <Key android:keyLabel="c" /> <Key android:keyLabel="v" /> <Key android:keyLabel="b" /> <Key android:keyLabel="n" /> <Key android:keyLabel="m" />
<Key android:codes="-5" android:isModifier="true" android:isRepeatable="true" android:keyEdgeFlags="right" android:keyIcon="@drawable/key_delete_icon" android:keyWidth="15%p" /> </Row>
<Row android:rowEdgeFlags="bottom"> <Key android:codes="-11" android:keyEdgeFlags="left" android:keyLabel="123" android:keyWidth="15%p" /> <Key android:codes="32" android:isRepeatable="true" android:keyLabel=" " android:keyWidth="70%p" /> <Key android:codes="-12" android:keyEdgeFlags="right" android:keyLabel="#+=" android:keyWidth="15%p" /> </Row> </Keyboard>
|
效果图如下:
2.1.2 数字键盘布局
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| <?xml version="1.0" encoding="utf-8"?> <Keyboard xmlns:android="http://schemas.android.com/apk/res/android" android:keyHeight="50dp" android:keyWidth="33.3%p"> <Row> <Key android:codes="49" android:keyLabel="1" /> <Key android:codes="50" android:keyLabel="2" /> <Key android:codes="51" android:keyLabel="3" /> </Row> <Row> <Key android:codes="52" android:keyLabel="4" /> <Key android:codes="53" android:keyLabel="5" /> <Key android:codes="54" android:keyLabel="6" /> </Row> <Row> <Key android:codes="55" android:keyLabel="7" /> <Key android:codes="56" android:keyLabel="8" /> <Key android:codes="57" android:keyLabel="9" /> </Row> <Row> <Key android:codes="-10" android:keyLabel="ABC" /> <Key android:codes="48" android:keyLabel="0" /> <Key android:codes="-12" android:keyLabel="#+=" /> </Row> </Keyboard>
|
效果图如下:
- Key之间的间隙不建议使用
horizontalGap
和verticalGap
设置。有兴趣可以尝试下,设置后会出现什么效果。此处采用drawable
的padding
属性。
keyLabel
属性只有一个字符时,当做输入键,keyLabel
有多个字符时,如果codes
也有多个值,仍然当做输入键,keyTextSize
值有效;只有一个值时当做功能键。labelTextSize
值有效。
codes
属性可以省略,默认使用keyLabel字符的Unicode值。功能键等其他自定义按键的 key code 建议设置为负数。
codes
有多个值时,单击取第一个,双击取第二个,三连击取第三个。通常建议该属性值不要超过3个。多个值用逗号分隔。
2.2 逻辑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| final Keyboard pinyin26KB = new Keyboard(this, R.xml.pinyin_26); final Keyboard numberKB = new Keyboard(this, R.xml.number);
keyboardView.setKeyboard(pinyin26KB); keyboardView.setOnKeyboardActionListener(new KeyboardView.OnKeyboardActionListener() {
@Override public void onPress(int primaryCode) { Log.d(TAG, "onPress: "+primaryCode); }
@Override public void onRelease(int primaryCode) { Log.d(TAG, "onRelease: "+primaryCode); }
@Override public void onKey(int primaryCode, int[] keyCodes) { Editable editable = edtInput.getText(); int start = edtInput.getSelectionStart(); switch (primaryCode) { case Keyboard.KEYCODE_SHIFT: pinyin26KB.setShifted(!pinyin26KB.isShifted()); keyboardView.invalidateAllKeys(); break; case Keyboard.KEYCODE_DELETE: if (editable != null && editable.length() > 0 && start > 0) { editable.delete(start - 1, start); } break; case -10: keyboardView.setKeyboard(pinyin26KB); break; case -11: keyboardView.setKeyboard(numberKB); break; case -12: break; default: if (primaryCode >= 97 && primaryCode <= 97 + 26) { editable.insert(start, pinyin26KB.isShifted() ? Character.toString((char) (primaryCode - 32)) : Character.toString((char) (primaryCode))); } else { editable.insert(start, Character.toString((char) (primaryCode))); } break; } }
@Override public void onText(CharSequence text) { Log.d(TAG, "onText: "+text); } });
|
已经定义的功能键code值,如下:
1 2 3 4 5 6 7
| public static final int KEYCODE_SHIFT = -1; public static final int KEYCODE_MODE_CHANGE = -2; public static final int KEYCODE_CANCEL = -3; public static final int KEYCODE_DONE = -4; public static final int KEYCODE_DELETE = -5; public static final int KEYCODE_ALT = -6;
|
Shift
键需要设置 isSticky
和isModifier
值为true
, codes
值为-1。
Delete
键需要设置isRepeatable
和isModifier
值为true
,codes
值为-5。
- 切换键盘的功能键需要自定义上述中未用到的code值,在
onKey
方法中做好对应的处理。
2.3 回显
1
| keyboardView.setPreviewEnabled(true);
|
打开回显,默认是true。
1 2 3
| android:keyPreviewLayout="@layout/preview" android:keyPreviewHeight="50dp" android:keyPreviewOffset="-20dp"
|
在键盘容器中声明以上属性。
2.4 备选小键盘
pupop_layout.xml
备选小键盘容器:
1 2 3 4 5 6 7 8 9 10 11 12 13
| <?xml version="1.0" encoding="utf-8"?> <android.inputmethodservice.KeyboardView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@android:id/keyboardView" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="#212121" android:keyBackground="@drawable/popup_bg" android:keyPreviewHeight="60dp" android:keyPreviewLayout="@layout/preview" android:keyPreviewOffset="-10dp" android:keyTextColor="#dddddd" android:keyTextSize="18sp" android:labelTextSize="18sp" />
|
pupop.xml
备选小键盘视图:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <?xml version="1.0" encoding="utf-8"?> <Keyboard xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@drawable/popup_bg" android:keyHeight="50dp">
<Row> <Key android:codes="97" /> <Key android:codes="98" /> <Key android:codes="99" /> </Row>
</Keyboard>
|
给w
键添加备选功能:
1 2 3 4
| <Key android:keyLabel="w" android:popupCharacters="123" android:popupKeyboard="@layout/pupop" />
|
在键盘容器中,指定备选小键盘布局:
1
| android:popupLayout="@layout/pupop_layout"
|
如果只声明了popupCharacters
,没有声明popupLayout
和popupKeyboard
,将会使用默认布局。只声明popupLayout
没声明popupKeyboard
,popupLayout
无效。
觉得有用?那打赏一个呗。[去打赏](/donate/)