原生的键盘布局声明了@deprecated
被废弃,虽然可以使用但明显不合需求。
/*** @deprecated This class is deprecated because this is just a convenient UI widget class that* application developers can re-implement on top of existing public APIs. If you have* already depended on this class, consider copying the implementation from AOSP into* your project or re-implementing a similar widget by yourselves*/
@Deprecated
public class KeyboardView extends View implements View.OnClickListener {
因此对KeyboardView的代码进行迁移和修改适配,形成自己项目特有的自定义View。
具体修改适配如下:
增加左上角增加小标签/小图标显示:
/** Small label to display */public CharSequence smallLabel;/** Small Icon to display instead of a small label. Small icon takes precedence over a small label */public Drawable smallIcon;
BKeyboard.java
中增加适配上和下按键,竖直摆放。verticalFlags
属性分为top
和bottom
,代表位于同一列。
/*** Flags that specify the anchoring to vertical of the key* that are just out of the boundary of the key. This is a bit mask of* {@link BKeyboard#VERTICAL_TOP} and {@link BKeyboard#VERTICAL_BOTTOM}.*/public int verticalFlags;
loadKeyboard
方法对verticalFlags
属性的按键特殊处理,verticalFlags
属性为top
和bottom
代表按键在同一列,计算为一个宽度,x
坐标不变,y
坐标偏移。
} else if (event == XmlResourceParser.END_TAG) {if (inKey) {inKey = false;if (key.verticalFlags == VERTICAL_TOP) {y += key.height + key.gap / 2;} else {x += key.gap + key.width;y = row * (currentRow.verticalGap + currentRow.defaultHeight);if (x > mTotalWidth) {mTotalWidth = x;}}} else if (inRow) {inRow = false;y += currentRow.verticalGap;y += currentRow.defaultHeight;row++;} else {// TODO: error or extend?}}
调整verticalFlags
属性按键的坐标轴位置,同一列x
不变。
final void resize(int newWidth, int newHeight) {int numRows = rows.size();for (int rowIndex = 0; rowIndex < numRows; ++rowIndex) {Row row = rows.get(rowIndex);int numKeys = row.mKeys.size();int totalGap = 0;int totalWidth = 0;for (int keyIndex = 0; keyIndex < numKeys; ++keyIndex) {Key key = row.mKeys.get(keyIndex);if (keyIndex > 0) {//By Evin, key align top or bottom of the bound as one keyif (key.verticalFlags != VERTICAL_TOP) {totalGap += key.gap;}}//By Evin, key align top or bottom of the bound as one keyif (key.verticalFlags != VERTICAL_TOP) {totalWidth += key.width;}}if (totalGap + totalWidth > newWidth) {int x = 0;float scaleFactor = (float)(newWidth - totalGap) / totalWidth;for (int keyIndex = 0; keyIndex < numKeys; ++keyIndex) {Key key = row.mKeys.get(keyIndex);key.width *= scaleFactor;//By Evin,verticalFlags for vertical keykey.x = x;if (key.verticalFlags != VERTICAL_TOP) {x += key.width + key.gap;}}}}mTotalWidth = newWidth;}
BKeyboardView.detectAndSendKey
方法增加快速点击的监听:
// Multi-tap
if (mInMultiTap) {if (mTapCount != -1) {mKeyboardActionListener.onKey(BKeyboard.KEYCODE_MULTI_TAP, new int[] {code});} else {mTapCount = 0;}code = key.codes[mTapCount];
}
增加适配可以变换如Caps Lock
的按键:
<!-- Whether this is a shifted key, such as Caps lock. --><attr name="isShifted" format="boolean" />
同时增加多点触控的适配使其能多个按键同时按下:
...final int action = me.getActionMasked();touchX = (int) me.getX(me.getActionIndex()) - getPaddingLeft();touchY = (int) me.getY(me.getActionIndex()) - getPaddingTop();...case MotionEvent.ACTION_POINTER_DOWN:...case MotionEvent.ACTION_POINTER_UP:...
按键表keyboard_normal.xml
如下:
<?xml version="1.0" encoding="utf-8"?>
<Keyboard xmlns:app="http://schemas.android.com/apk/res-auto"app:horizontalGap="0.8%p"app:keyWidth="10%p"app:keyHeight="46dp"app:verticalGap="2%p"><Row><Keyapp:codes="41"app:keyWidth="8%p"app:keyEdgeFlags="left"app:keyLabel="esc" /><Keyapp:codes="30"app:keyWidth="6%p"app:keyLabel="f1" /><Keyapp:codes="31"app:keyWidth="6%p"app:keyLabel="f2" /><Keyapp:codes="32"app:keyWidth="6%p"app:keyLabel="f3" /><Keyapp:codes="33"app:keyWidth="6%p"app:keyLabel="f4" /><Keyapp:codes="34"app:keyWidth="6%p"app:keyLabel="f5" /><Keyapp:codes="35"app:keyWidth="6%p"app:keyLabel="f6" /><Keyapp:codes="36"app:keyWidth="6%p"app:keyLabel="f7" /><Keyapp:codes="37"app:keyWidth="6%p"app:keyLabel="f8" /><Keyapp:codes="38"app:keyWidth="6%p"app:keyLabel="f9" /><Keyapp:codes="39"app:keyWidth="6%p"app:keyLabel="f10" /><Keyapp:codes="45"app:keyWidth="6%p"app:keyLabel="f11" /><Keyapp:codes="46"app:keyWidth="6%p"app:keyLabel="f12" /><Keyapp:codes="76"app:keyWidth="8%p"app:keyEdgeFlags="right"app:isRepeatable="true"app:keyLabel="del" /></Row><Row><Keyapp:codes="50"app:keyWidth="6%p"app:keyEdgeFlags="left"app:keySmallLabel="~"app:keyLabel="`" /><Keyapp:codes="30"app:keyWidth="6%p"app:keySmallLabel="!"app:keyLabel="1" /><Keyapp:codes="31"app:keyWidth="6%p"app:keySmallLabel="\@"app:keyLabel="2" /><Keyapp:codes="32"app:keyWidth="6%p"app:keySmallLabel="#"app:keyLabel="3" /><Keyapp:codes="33"app:keyWidth="6%p"app:keySmallLabel="¥"app:keyLabel="4" /><Keyapp:codes="34"app:keyWidth="6%p"app:keySmallLabel="%"app:keyLabel="5" /><Keyapp:codes="35"app:keyWidth="6%p"app:keySmallLabel="^"app:keyLabel="6" /><Keyapp:codes="36"app:keyWidth="6%p"app:keySmallLabel="&"app:keyLabel="7" /><Keyapp:codes="37"app:keyWidth="6%p"app:keySmallLabel="*"app:keyLabel="8" /><Keyapp:codes="38"app:keyWidth="6%p"app:keySmallLabel="("app:keyLabel="9" /><Keyapp:codes="39"app:keyWidth="6%p"app:keySmallLabel=")"app:keyLabel="0" /><Keyapp:codes="45"app:keyWidth="6%p"app:keySmallLabel="—"app:keyLabel="-" /><Keyapp:codes="46"app:keyWidth="6%p"app:keySmallLabel="+"app:keyLabel="=" /><Keyapp:codes="42"app:keyWidth="10%p"app:keyEdgeFlags="right"app:isRepeatable="true"app:keyLabel="back" /></Row><Row><Keyapp:codes="43"app:keyWidth="10%p"app:keyEdgeFlags="left"app:keyLabel="tab" /><Keyapp:codes="20"app:keyWidth="6%p"app:keyLabel="Q" /><Keyapp:codes="26"app:keyWidth="6%p"app:keyLabel="W" /><Keyapp:codes="8"app:keyWidth="6%p"app:keyLabel="E" /><Keyapp:codes="21"app:keyWidth="6%p"app:keyLabel="R" /><Keyapp:codes="23"app:keyWidth="6%p"app:keyLabel="T" /><Keyapp:codes="28"app:keyWidth="6%p"app:keyLabel="Y" /><Keyapp:codes="24"app:keyWidth="6%p"app:keyLabel="U" /><Keyapp:codes="12"app:keyWidth="6%p"app:keyLabel="I" /><Keyapp:codes="18"app:keyWidth="6%p"app:keyLabel="O" /><Keyapp:codes="19"app:keyWidth="6%p"app:keyLabel="P" /><Keyapp:codes="47"app:keyWidth="6%p"app:keySmallLabel="{"app:keyLabel="[" /><Keyapp:codes="48"app:keyWidth="6%p"app:keySmallLabel="}"app:keyLabel="]" /><Keyapp:codes="49"app:keyWidth="6%p"app:keyEdgeFlags="right"app:keySmallLabel="|"app:keyLabel="\\" /></Row><Row><Keyapp:codes="57"app:keyWidth="11.4%p"app:keyEdgeFlags="left"app:isShifted="true"app:keyLabel="caps" /><Keyapp:codes="4"app:keyWidth="6%p"app:keyLabel="A" /><Keyapp:codes="22"app:keyWidth="6%p"app:keyLabel="S" /><Keyapp:codes="7"app:keyWidth="6%p"app:keyLabel="D" /><Keyapp:codes="9"app:keyWidth="6%p"app:keyLabel="F" /><Keyapp:codes="10"app:keyWidth="6%p"app:keyLabel="G" /><Keyapp:codes="11"app:keyWidth="6%p"app:keyLabel="H" /><Keyapp:codes="13"app:keyWidth="6%p"app:keyLabel="J" /><Keyapp:codes="14"app:keyWidth="6%p"app:keyLabel="K" /><Keyapp:codes="15"app:keyWidth="6%p"app:keyLabel="L" /><Keyapp:codes="51"app:keyWidth="6%p"app:keySmallLabel=':'app:keyLabel=";" /><Keyapp:codes="52"app:keyWidth="6%p"app:keySmallLabel='"'app:keyLabel="'" /><Keyapp:codes="40"app:keyWidth="11.4%p"app:keyLabel="enter"app:keyEdgeFlags="right"/></Row><Row><Keyapp:codes="-1001"app:keyWidth="14.8%p"app:keyEdgeFlags="left"app:isModifier="true"app:keyLabel="shift" /><Keyapp:codes="29"app:keyWidth="6%p"app:keyLabel="Z" /><Keyapp:codes="27"app:keyWidth="6%p"app:keyLabel="X" /><Keyapp:codes="6"app:keyWidth="6%p"app:keyLabel="C" /><Keyapp:codes="25"app:keyWidth="6%p"app:keyLabel="V" /><Keyapp:codes="5"app:keyWidth="6%p"app:keyLabel="B" /><Keyapp:codes="17"app:keyWidth="6%p"app:keyLabel="N" /><Keyapp:codes="16"app:keyWidth="6%p"app:keyLabel="M" /><Keyapp:codes="16"app:keyWidth="6%p"app:keySmallLabel="<"app:keyLabel="," /><Keyapp:codes="16"app:keyWidth="6%p"app:keySmallLabel=">"app:keyLabel="." /><Keyapp:codes="16"app:keyWidth="6%p"app:keySmallLabel='?'app:keyLabel="/" /><Keyapp:codes="-1"app:keyLabel="shift"app:keyEdgeFlags="right"app:keyWidth="14.8%p" /></Row><Row><Keyapp:codes="-1004"app:keyWidth="6%p"app:keyEdgeFlags="left"app:isModifier="true"app:keyLabel="ctrl" /><Keyapp:codes="-1006"app:keyWidth="6%p"app:isModifier="true"app:keyLabel="fn" /><Keyapp:codes="-1005"app:keyWidth="6%p"app:isModifier="true"app:keyIcon="@drawable/ic_win" /><Keyapp:codes="-1006"app:keyWidth="6%p"app:isModifier="true"app:keyLabel="alt" /><Keyapp:codes="44"app:keyWidth="37.2%p"app:keyLabel="space"app:isRepeatable="true"/><Keyapp:codes="-1006"app:keyWidth="6%p"app:isModifier="true"app:keyLabel="alt" /><Keyapp:codes="-1006"app:keyWidth="6%p"app:isModifier="true"app:keyLabel="ctrl" /><Keyapp:codes="80"app:keyWidth="6%p"app:isModifier="true"app:keyLabel="◀︎" /><Keyapp:codes="82"app:keyVerticalFlags="top"app:keyWidth="6%p"app:isModifier="true"app:keyLabel="▲" /><Keyapp:codes="81"app:keyWidth="6%p"app:keyVerticalFlags="bottom"app:isModifier="true"app:keyLabel="▼" /><Keyapp:codes="79"app:keyWidth="6%p"app:isModifier="true"app:keyEdgeFlags="right"app:keyLabel="▶︎︎" /></Row>
</Keyboard>
用法和KeyboardView类似,只不过需要声明 xmlns:app="http://schemas.android.com/apk/res-auto"
的app属性作用域,以免与系统的android属性冲突。
当然还有一些细节修改,此处不贴代码了。
使用方式如下:
BKeyboardView keyboardView = findViewById(R.id.keyboard_view);keyboardView.setPreviewEnabled(false); // 取消按键弹框的显示keyboardView.setOnKeyboardActionListener(new BKeyboardView.OnKeyboardActionListener() {@Overridepublic void onPress(int primaryCode) {}@Overridepublic void onRelease(int primaryCode) {}@Overridepublic void onKey(int primaryCode, int[] keyCodes) {sendKey(primaryCode);playClick(primaryCode);}@Overridepublic void onText(CharSequence text) {}@Overridepublic void swipeLeft() {}@Overridepublic void swipeRight() {}@Overridepublic void swipeDown() {}@Overridepublic void swipeUp() {}});
可以自定义播放不同的按键声音:
private void playClick(int keyCode){//Todo play different click audio for different keycodemAudioManager.playSoundEffect(AudioManager.FX_KEYPRESS_STANDARD);// mAudioManager.playSoundEffect(AudioManager.FX_KEYPRESS_SPACEBAR);// mAudioManager.playSoundEffect(AudioManager.FX_KEYPRESS_RETURN);// mAudioManager.playSoundEffect(AudioManager.FX_KEYPRESS_DELETE);}
仿真键盘效果如下: