原生的键盘布局声明了@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 key
if (key.verticalFlags != VERTICAL_TOP) {
totalGap += key.gap;
}
}
//By Evin, key align top or bottom of the bound as one key
if (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 key
key.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>
<Key
app:codes="41"
app:keyWidth="8%p"
app:keyEdgeFlags="left"
app:keyLabel="esc" />
<Key
app:codes="30"
app:keyWidth="6%p"
app:keyLabel="f1" />
<Key
app:codes="31"
app:keyWidth="6%p"
app:keyLabel="f2" />
<Key
app:codes="32"
app:keyWidth="6%p"
app:keyLabel="f3" />
<Key
app:codes="33"
app:keyWidth="6%p"
app:keyLabel="f4" />
<Key
app:codes="34"
app:keyWidth="6%p"
app:keyLabel="f5" />
<Key
app:codes="35"
app:keyWidth="6%p"
app:keyLabel="f6" />
<Key
app:codes="36"
app:keyWidth="6%p"
app:keyLabel="f7" />
<Key
app:codes="37"
app:keyWidth="6%p"
app:keyLabel="f8" />
<Key
app:codes="38"
app:keyWidth="6%p"
app:keyLabel="f9" />
<Key
app:codes="39"
app:keyWidth="6%p"
app:keyLabel="f10" />
<Key
app:codes="45"
app:keyWidth="6%p"
app:keyLabel="f11" />
<Key
app:codes="46"
app:keyWidth="6%p"
app:keyLabel="f12" />
<Key
app:codes="76"
app:keyWidth="8%p"
app:keyEdgeFlags="right"
app:isRepeatable="true"
app:keyLabel="del" />
</Row>
<Row>
<Key
app:codes="50"
app:keyWidth="6%p"
app:keyEdgeFlags="left"
app:keySmallLabel="~"
app:keyLabel="`" />
<Key
app:codes="30"
app:keyWidth="6%p"
app:keySmallLabel="!"
app:keyLabel="1" />
<Key
app:codes="31"
app:keyWidth="6%p"
app:keySmallLabel="\@"
app:keyLabel="2" />
<Key
app:codes="32"
app:keyWidth="6%p"
app:keySmallLabel="#"
app:keyLabel="3" />
<Key
app:codes="33"
app:keyWidth="6%p"
app:keySmallLabel="¥"
app:keyLabel="4" />
<Key
app:codes="34"
app:keyWidth="6%p"
app:keySmallLabel="%"
app:keyLabel="5" />
<Key
app:codes="35"
app:keyWidth="6%p"
app:keySmallLabel="^"
app:keyLabel="6" />
<Key
app:codes="36"
app:keyWidth="6%p"
app:keySmallLabel="&"
app:keyLabel="7" />
<Key
app:codes="37"
app:keyWidth="6%p"
app:keySmallLabel="*"
app:keyLabel="8" />
<Key
app:codes="38"
app:keyWidth="6%p"
app:keySmallLabel="("
app:keyLabel="9" />
<Key
app:codes="39"
app:keyWidth="6%p"
app:keySmallLabel=")"
app:keyLabel="0" />
<Key
app:codes="45"
app:keyWidth="6%p"
app:keySmallLabel="—"
app:keyLabel="-" />
<Key
app:codes="46"
app:keyWidth="6%p"
app:keySmallLabel="+"
app:keyLabel="=" />
<Key
app:codes="42"
app:keyWidth="10%p"
app:keyEdgeFlags="right"
app:isRepeatable="true"
app:keyLabel="back" />
</Row>
<Row>
<Key
app:codes="43"
app:keyWidth="10%p"
app:keyEdgeFlags="left"
app:keyLabel="tab" />
<Key
app:codes="20"
app:keyWidth="6%p"
app:keyLabel="Q" />
<Key
app:codes="26"
app:keyWidth="6%p"
app:keyLabel="W" />
<Key
app:codes="8"
app:keyWidth="6%p"
app:keyLabel="E" />
<Key
app:codes="21"
app:keyWidth="6%p"
app:keyLabel="R" />
<Key
app:codes="23"
app:keyWidth="6%p"
app:keyLabel="T" />
<Key
app:codes="28"
app:keyWidth="6%p"
app:keyLabel="Y" />
<Key
app:codes="24"
app:keyWidth="6%p"
app:keyLabel="U" />
<Key
app:codes="12"
app:keyWidth="6%p"
app:keyLabel="I" />
<Key
app:codes="18"
app:keyWidth="6%p"
app:keyLabel="O" />
<Key
app:codes="19"
app:keyWidth="6%p"
app:keyLabel="P" />
<Key
app:codes="47"
app:keyWidth="6%p"
app:keySmallLabel="{"
app:keyLabel="[" />
<Key
app:codes="48"
app:keyWidth="6%p"
app:keySmallLabel="}"
app:keyLabel="]" />
<Key
app:codes="49"
app:keyWidth="6%p"
app:keyEdgeFlags="right"
app:keySmallLabel="|"
app:keyLabel="\\" />
</Row>
<Row>
<Key
app:codes="57"
app:keyWidth="11.4%p"
app:keyEdgeFlags="left"
app:isShifted="true"
app:keyLabel="caps" />
<Key
app:codes="4"
app:keyWidth="6%p"
app:keyLabel="A" />
<Key
app:codes="22"
app:keyWidth="6%p"
app:keyLabel="S" />
<Key
app:codes="7"
app:keyWidth="6%p"
app:keyLabel="D" />
<Key
app:codes="9"
app:keyWidth="6%p"
app:keyLabel="F" />
<Key
app:codes="10"
app:keyWidth="6%p"
app:keyLabel="G" />
<Key
app:codes="11"
app:keyWidth="6%p"
app:keyLabel="H" />
<Key
app:codes="13"
app:keyWidth="6%p"
app:keyLabel="J" />
<Key
app:codes="14"
app:keyWidth="6%p"
app:keyLabel="K" />
<Key
app:codes="15"
app:keyWidth="6%p"
app:keyLabel="L" />
<Key
app:codes="51"
app:keyWidth="6%p"
app:keySmallLabel=':'
app:keyLabel=";" />
<Key
app:codes="52"
app:keyWidth="6%p"
app:keySmallLabel='"'
app:keyLabel="'" />
<Key
app:codes="40"
app:keyWidth="11.4%p"
app:keyLabel="enter"
app:keyEdgeFlags="right"/>
</Row>
<Row>
<Key
app:codes="-1001"
app:keyWidth="14.8%p"
app:keyEdgeFlags="left"
app:isModifier="true"
app:keyLabel="shift" />
<Key
app:codes="29"
app:keyWidth="6%p"
app:keyLabel="Z" />
<Key
app:codes="27"
app:keyWidth="6%p"
app:keyLabel="X" />
<Key
app:codes="6"
app:keyWidth="6%p"
app:keyLabel="C" />
<Key
app:codes="25"
app:keyWidth="6%p"
app:keyLabel="V" />
<Key
app:codes="5"
app:keyWidth="6%p"
app:keyLabel="B" />
<Key
app:codes="17"
app:keyWidth="6%p"
app:keyLabel="N" />
<Key
app:codes="16"
app:keyWidth="6%p"
app:keyLabel="M" />
<Key
app:codes="16"
app:keyWidth="6%p"
app:keySmallLabel="<"
app:keyLabel="," />
<Key
app:codes="16"
app:keyWidth="6%p"
app:keySmallLabel=">"
app:keyLabel="." />
<Key
app:codes="16"
app:keyWidth="6%p"
app:keySmallLabel='?'
app:keyLabel="/" />
<Key
app:codes="-1"
app:keyLabel="shift"
app:keyEdgeFlags="right"
app:keyWidth="14.8%p" />
</Row>
<Row>
<Key
app:codes="-1004"
app:keyWidth="6%p"
app:keyEdgeFlags="left"
app:isModifier="true"
app:keyLabel="ctrl" />
<Key
app:codes="-1006"
app:keyWidth="6%p"
app:isModifier="true"
app:keyLabel="fn" />
<Key
app:codes="-1005"
app:keyWidth="6%p"
app:isModifier="true"
app:keyIcon="@drawable/ic_win" />
<Key
app:codes="-1006"
app:keyWidth="6%p"
app:isModifier="true"
app:keyLabel="alt" />
<Key
app:codes="44"
app:keyWidth="37.2%p"
app:keyLabel="space"
app:isRepeatable="true"/>
<Key
app:codes="-1006"
app:keyWidth="6%p"
app:isModifier="true"
app:keyLabel="alt" />
<Key
app:codes="-1006"
app:keyWidth="6%p"
app:isModifier="true"
app:keyLabel="ctrl" />
<Key
app:codes="80"
app:keyWidth="6%p"
app:isModifier="true"
app:keyLabel="◀︎" />
<Key
app:codes="82"
app:keyVerticalFlags="top"
app:keyWidth="6%p"
app:isModifier="true"
app:keyLabel="▲" />
<Key
app:codes="81"
app:keyWidth="6%p"
app:keyVerticalFlags="bottom"
app:isModifier="true"
app:keyLabel="▼" />
<Key
app: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() {
@Override
public void onPress(int primaryCode) {
}
@Override
public void onRelease(int primaryCode) {
}
@Override
public void onKey(int primaryCode, int[] keyCodes) {
sendKey(primaryCode);
playClick(primaryCode);
}
@Override
public void onText(CharSequence text) {
}
@Override
public void swipeLeft() {
}
@Override
public void swipeRight() {
}
@Override
public void swipeDown() {
}
@Override
public void swipeUp() {
}
});
可以自定义播放不同的按键声音:
private void playClick(int keyCode){
//Todo play different click audio for different keycode
mAudioManager.playSoundEffect(AudioManager.FX_KEYPRESS_STANDARD);
// mAudioManager.playSoundEffect(AudioManager.FX_KEYPRESS_SPACEBAR);
// mAudioManager.playSoundEffect(AudioManager.FX_KEYPRESS_RETURN);
// mAudioManager.playSoundEffect(AudioManager.FX_KEYPRESS_DELETE);
}
仿真键盘效果如下: