实现思路:
1.自定义RandomKeyboard,继承系统keyboard
2.自定义keyboardViewGroup(线性布局,含一级子控件NestedScrollView和自定义RandomKeyboard),重新addview方法使添加的子控件添加到NestedScrollView里。可在这个控件初始化时屏蔽系统输入法,监听事件分发通过scrollview中findFocus获取当前有焦点的edittext控件并绑定RandomKeyboard
核心代码
RandomKeyboard
public void onKey(int primaryCode, int[] keyCodes) {
Editable editable = null;
int selectionStart = 0;
if (editText != null) {
editable = editText.getText();
selectionStart = editText.getSelectionStart();
}
if (keyboard != null) {
switch (primaryCode) {
case Keyboard.KEYCODE_SHIFT://shift处理 大小写字母切换
keyboard.setShifted(!keyboard.isShifted());
invalidateAllKeys();
break;
case Keyboard.KEYCODE_DELETE://删除处理
if (editable != null && editable.length() > 0 && selectionStart > 0) {
editable.delete(selectionStart - 1, selectionStart);
}
break;
....
自定义键盘操作
....
case ENTER_KEYBOARD://enter处理
if (editText != null && editText.getImeOptions() == EditorInfo.IME_ACTION_NEXT) {
@SuppressLint("WrongConstant") View view = editText.focusSearch(FOCUS_FORWARD);
editText.onEditorAction(EditorInfo.IME_ACTION_NEXT);
if (view instanceof EditText) {
bindEditText((EditText) view);
}
} else {
if (editText != null && editText.hasFocus()) {
editText.clearFocus();
}
hide();
}
break;
default:
if (primaryCode >= 97 && primaryCode <= 97 + 26) {
if (editText != null) {
editable.insert(selectionStart,
keyboard.isShifted() ? Character.toString((char) (primaryCode - 32))
: Character.toString((char) primaryCode));
}
} else {
if (editable != null) {
editable.insert(
selectionStart,
Character.toString((char) primaryCode));
}
}
break;
}
}
}
private RandomKeyboard setRandom() {
if (keyType == TYPE_SYMBOL || keyType == TYPE_LETTER_AND_NUM) {
return this;
}
if (keyboard != null) {
List<Keyboard.Key> newKeys = keyboard.getKeys();
if (checkList(newKeys)) {
containerKeys.clear();
resultKeys.clear();
//筛选并缓存可随机的key
for (Keyboard.Key key : newKeys) {
if (key != null && isNeedRandom(key.label)) {
containerKeys.add(new KeyModel(key.codes, key.label));
}
}
//随机打乱筛选出的key
Random rand = new SecureRandom();
// rand.setSeed(SystemClock.currentThreadTimeMillis());
int size = containerKeys.size();
for (int i = 0; i < size; i++) {
int num = rand.nextInt(size - i);
resultKeys.add(containerKeys.get(num));
containerKeys.remove(num);
}
//将打乱的key赋值给键盘空位(第一步中取值的位置)
int count = 0;
for (Keyboard.Key key : newKeys) {
if (key != null && isNeedRandom(key.label) && checkListItem(resultKeys, count)) {
KeyModel randomKey = resultKeys.get(count);
key.label = randomKey.getLable();
key.codes = randomKey.getCodes();
count++;
}
}
invalidateAllKeys();
isRandom = true;
}
}
return this;
}
RandomKeyboardViewGroup
//禁用系统输入法
if (context instanceof Activity) {
Window window = ((Activity) context).getWindow();
((Activity) context).getWindow().setFlags(
WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
boolean touchEvent = super.dispatchTouchEvent(ev);
View editFocus = findEditFocus();
if (keyboard != null) {
if (editFocus != null) {
if (ViewUtils.isOutsideTheView(editFocus, ev) && ViewUtils.isOutsideTheView(keyboard, ev)) {
editFocus.clearFocus();
keyboard.hide();
} else {
keyboard.bindEditText((EditText) editFocus);
keyboard.show();
}
} else {
keyboard.hide();
}
}
return touchEvent;
}
public View findEditFocus() {
View currentFocus = slContent != null ? slContent.findFocus() : null;
return currentFocus instanceof EditText ? currentFocus : null;
}
自定义RandomKeyboard
继承自KeyboardView
实现OnKeyboardActionListener监听
重写 public void onKey(int primaryCode, int[] keyCodes) 方法控制软键盘操作
public class RandomKeyboard extends KeyboardView implements KeyboardView.OnKeyboardActionListener, LifecycleObserver {
private static final int LETTER_KEYBOARD = -10;
private static final int NUMBER_KEYBOARD = -11;
private static final int SYMBOL_KEYBOARD = -12;
private static final int ENTER_KEYBOARD = -13;
private Context context;
private Keyboard keyboard;
private EditText editText;
private boolean isRandom = false;
private boolean isNeedRandom = false;
private int keyType;
private int defLetterType = TYPE_LETTER;
private int defNumberType = TYPE_NUMBER;
private List<KeyModel> containerKeys = new ArrayList<>();
private List<KeyModel> resultKeys = new ArrayList<>();
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
public void onDestroy() {
if (containerKeys instanceof ArrayList) {
containerKeys.clear();
containerKeys = null;
}
if (resultKeys instanceof ArrayList) {
resultKeys.clear();
resultKeys = null;
}
containerKeys = null;
resultKeys = null;
if (context instanceof LifecycleOwner) {
((LifecycleOwner) context).getLifecycle().removeObserver(this);
}
context = null;
editText = null;
}
public RandomKeyboard(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public RandomKeyboard(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.context = context;
if (context instanceof LifecycleOwner) {
((LifecycleOwner) context).getLifecycle().addObserver(this);
}
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RandomKeyboard);
isNeedRandom = typedArray.getBoolean(R.styleable.RandomKeyboard_isRandom, false);
int anInt = typedArray.getInt(R.styleable.RandomKeyboard_keyboardType, TYPE_LETTER);
typedArray.recycle();
setKeyboard(anInt);
setOnKeyboardActionListener(this);
hide();
}
/**
* 绑定EditText
*/
public RandomKeyboard bindEditText(EditText editText) {
this.editText = editText;
if (editText != null) {
setVisibility(editText.hasFocus() ? VISIBLE : GONE);
int imeOptions = editText.getImeOptions();
List<Keyboard.Key> keys = CheckValueUtils.checkNotNull(keyboard).getKeys();
for (int i = 0; i < checkList(keys, "").size(); i++) {
Keyboard.Key key = keys.get(i);
if (key.codes[0] == -13) {
key.label = imeOptions == EditorInfo.IME_ACTION_NEXT ? "下一步" : "完成";
invalidateKey(i);
return this;
}
}
}
return this;
}
/**
* @see Constant.KEYBOARD_TYPE
*/
public RandomKeyboard setKeyboard(@KeyboardType int type) {
keyType = type;
switch (type) {
case TYPE_LETTER:
defLetterType = TYPE_LETTER;
keyboard = new Keyboard(context, R.xml.letter_keyboard_layout);
break;
case TYPE_NUMBER:
defNumberType = TYPE_NUMBER;
keyboard = new Keyboard(context, R.xml.number_keyboard_layout);
break;
case TYPE_SYMBOL:
keyboard = new Keyboard(context, R.xml.symbol_keyboard_layout);
break;
case TYPE_LETTER_AND_NUM:
defLetterType = TYPE_LETTER_AND_NUM;
keyboard = new Keyboard(context, R.xml.letter_num_keyboard_layout);
break;
case TYPE_NUM_AND_SYMBOL:
defNumberType = TYPE_NUM_AND_SYMBOL;
keyboard = new Keyboard(context, R.xml.num_symbol_keyboard_layout);
break;
default:
keyboard = new Keyboard(context, R.xml.letter_keyboard_layout);
break;
}
setKeyboard(keyboard);
isRandom(isNeedRandom);
return this;
}
public Keyboard getKeyboard() {
return keyboard;
}
@Override
public void onKey(int primaryCode, int[] keyCodes) {
Editable editable = null;
int selectionStart = 0;
if (editText != null) {
editable = editText.getText();
selectionStart = editText.getSelectionStart();
}
if (keyboard != null) {
switch (primaryCode) {
case Keyboard.KEYCODE_SHIFT://shift处理 大小写字母切换
keyboard.setShifted(!keyboard.isShifted());
invalidateAllKeys();
break;
case Keyboard.KEYCODE_DELETE://删除处理
if (editable != null && editable.length() > 0 && selectionStart > 0) {
editable.delete(selectionStart - 1, selectionStart);
}
break;
case LETTER_KEYBOARD:
setKeyboard(defLetterType);
break;
case NUMBER_KEYBOARD:
setKeyboard(defNumberType);
break;
case SYMBOL_KEYBOARD:
setKeyboard(TYPE_SYMBOL);
break;
case ENTER_KEYBOARD://enter处理
if (editText != null && editText.getImeOptions() == EditorInfo.IME_ACTION_NEXT) {
@SuppressLint("WrongConstant") View view = editText.focusSearch(FOCUS_FORWARD);
editText.onEditorAction(EditorInfo.IME_ACTION_NEXT);
if (view instanceof EditText) {
bindEditText((EditText) view);
}
} else {
if (editText != null && editText.hasFocus()) {
editText.clearFocus();
}
hide();
}
break;
default:
if (primaryCode >= 97 && primaryCode <= 97 + 26) {
if (editText != null) {
editable.insert(selectionStart,
keyboard.isShifted() ? Character.toString((char) (primaryCode - 32))
: Character.toString((char) primaryCode));
}
} else {
if (editable != null) {
editable.insert(
selectionStart,
Character.toString((char) primaryCode));
}
}
break;
}
}
}
@Override
public void onPress(int primaryCode) {
}
@Override
public void onRelease(int primaryCode) {
}
@Override
public void onText(CharSequence text) {
}
@Override
public void swipeLeft() {
}
@Override
public void swipeRight() {
}
@Override
public void swipeDown() {
}
@Override
public void swipeUp() {
}
/**
* @see #setKeyboard(int)
*/
@Override
@Deprecated
public final void setKeyboard(Keyboard keyboard) {
isRandom = false;
super.setKeyboard(keyboard);
}
/**
* 随机打乱key键
*
* @see Constant.KEYBOARD_TYPE#TYPE_SYMBOL 字符模式不可用
* @see Constant.KEYBOARD_TYPE#TYPE_LETTER_AND_NUM 字母数字模式不可用
*/
public RandomKeyboard isRandom(boolean isRandom) {
isNeedRandom = isRandom;
if (this.isRandom == isRandom) {
return this;
}
if (isRandom) {
setRandom();
} else {
setKeyboard(keyType);
}
return this;
}
public boolean isRandom() {
return isRandom;
}
/**
* 随机打乱key键
*
* @see Constant.KEYBOARD_TYPE#TYPE_SYMBOL 字符模式不可用
* @see Constant.KEYBOARD_TYPE#TYPE_LETTER_AND_NUM 字母数字模式不可用
*/
private RandomKeyboard setRandom() {
if (keyType == TYPE_SYMBOL || keyType == TYPE_LETTER_AND_NUM) {
return this;
}
if (keyboard != null) {
List<Keyboard.Key> newKeys = keyboard.getKeys();
if (checkList(newKeys)) {
containerKeys.clear();
resultKeys.clear();
//筛选并缓存可随机的key
for (Keyboard.Key key : newKeys) {
if (key != null && isNeedRandom(key.label)) {
containerKeys.add(new KeyModel(key.codes, key.label));
}
}
//随机打乱筛选出的key
Random rand = new SecureRandom();
// rand.setSeed(SystemClock.currentThreadTimeMillis());
int size = containerKeys.size();
for (int i = 0; i < size; i++) {
int num = rand.nextInt(size - i);
resultKeys.add(containerKeys.get(num));
containerKeys.remove(num);
}
//将打乱的key赋值给键盘空位(第一步中取值的位置)
int count = 0;
for (Keyboard.Key key : newKeys) {
if (key != null && isNeedRandom(key.label) && checkListItem(resultKeys, count)) {
KeyModel randomKey = resultKeys.get(count);
key.label = randomKey.getLable();
key.codes = randomKey.getCodes();
count++;
}
}
invalidateAllKeys();
isRandom = true;
}
}
return this;
}
private boolean isNeedRandom(CharSequence s) {
return !TextUtils.isEmpty(s) && s.length() == 1 && (CheckValueUtils.isNumber(s.toString()) || CheckValueUtils.isLetter(s.toString()));
}
public RandomKeyboard show() {
if (getVisibility() != VISIBLE) {
setVisibility(VISIBLE);
}
return this;
}
public RandomKeyboard hide() {
if (getVisibility() != GONE || getVisibility() != INVISIBLE) {
setVisibility(GONE);
}
return this;
}
}
自定义keyboardViewGroup
使用方法:在activity布局文件中使用,位置:可做根布局、或置于toolbar之下到根布局底部,可假定为NestedScrollView使用。
注意:
1、keyboardViewGroup中有且只有一个子布局(因这里的布局会加载到NestedScrollView 中)。
2、无需手动绑定EditText,RandomKeyboardViewGroup 自动查找有焦点的EditText并绑定。
public class RandomKeyboardViewGroup extends FrameLayout implements LifecycleObserver {
private Context context;
private View inflate;
private NestedScrollView slContent;
private RandomKeyboard keyboard;
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
public void onDestroy() {
if (slContent != null) {
slContent.removeAllViews();
slContent = null;
}
inflate = null;
if (context instanceof LifecycleOwner) {
((LifecycleOwner) context).getLifecycle().removeObserver(this);
}
context = null;
if (keyboard != null) {
keyboard.onDestroy();
keyboard = null;
}
}
public RandomKeyboardViewGroup(Context context) {
this(context, null);
}
public RandomKeyboardViewGroup(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public RandomKeyboardViewGroup(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.context = context;
if (context instanceof LifecycleOwner) {
((LifecycleOwner) context).getLifecycle().addObserver(this);
}
inflate = View.inflate(context, R.layout.random_keyboard_viewgroup, null);
slContent = ViewUtils.findViewById(inflate, R.id.sl_content);
keyboard = ViewUtils.findViewById(inflate, R.id.random_keyboard);
addView(inflate);
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RandomKeyboardViewGroup);
boolean aBoolean = typedArray.getBoolean(R.styleable.RandomKeyboardViewGroup_isRandom, false);
int anInt = typedArray.getInt(R.styleable.RandomKeyboardViewGroup_keyboardType, TYPE_LETTER);
typedArray.recycle();
isRandom(aBoolean);
setKeyboard(anInt);
//禁用系统输入法
if (context instanceof Activity) {
Window window = ((Activity) context).getWindow();
((Activity) context).getWindow().setFlags(
WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
}
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
boolean touchEvent = super.dispatchTouchEvent(ev);
View editFocus = findEditFocus();
if (keyboard != null) {
if (editFocus != null) {
if (ViewUtils.isOutsideTheView(editFocus, ev) && ViewUtils.isOutsideTheView(keyboard, ev)) {
editFocus.clearFocus();
keyboard.hide();
} else {
keyboard.bindEditText((EditText) editFocus);
keyboard.show();
}
} else {
keyboard.hide();
}
}
return touchEvent;
}
public View findEditFocus() {
View currentFocus = slContent != null ? slContent.findFocus() : null;
return currentFocus instanceof EditText ? currentFocus : null;
}
@Override
public void addView(View child, int index, ViewGroup.LayoutParams params) {
if (child.getId() == inflate.getId()) {
super.addView(child, index, params);
} else {
if (slContent != null) {
slContent.addView(child, index, params);
}
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
ViewGroup.LayoutParams layoutParams = getLayoutParams();
layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT;
layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT;
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (inflate != null) {
ViewGroup.LayoutParams layoutParams1 = inflate.getLayoutParams();
layoutParams1.width = getMeasuredWidth();
layoutParams1.height = getMeasuredHeight();
inflate.setLayoutParams(layoutParams1);
}
if (slContent != null && keyboard != null) {
slContent.setMinimumWidth(getMeasuredHeight() - keyboard.getMeasuredHeight());
}
}
/**
* 打开关闭软键盘
*/
public void KeyboardToggle(boolean isShow) {
if (keyboard != null) {
if (isShow) {
keyboard.show();
} else {
keyboard.hide();
}
}
}
/**
* 打开关闭软键盘
*/
public void KeyboardToggle(EditText editText) {
if (keyboard != null && editText != null) {
keyboard.bindEditText(editText);
if (editText.hasFocus()) {
keyboard.show();
} else {
keyboard.hide();
}
}
}
/**
* @see Constant.KEYBOARD_TYPE
* @see RandomKeyboard#setKeyboard(int)
*/
public void setKeyboard(@KeyboardType int type) {
if (keyboard != null) {
keyboard.setKeyboard(type);
}
}
/**
* 随机打乱key-value
* @see RandomKeyboard#isRandom(boolean)
*/
public void isRandom(boolean isRandom) {
if (keyboard != null) {
keyboard.isRandom(isRandom);
}
}
public boolean isRandom() {
return keyboard != null && keyboard.isRandom();
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
if (null != keyboard && keyboard.getVisibility() == VISIBLE) {
keyboard.hide();
return true;
}
}
return super.onKeyDown(keyCode, event);
}
}
其他资源
KeyModel.class
data class KeyModel(var codes: IntArray, var lable: CharSequence)
KEYBOARD_TYPE
public interface Constant {
interface KEYBOARD_TYPE {
int TYPE_LETTER = 1;
int TYPE_NUMBER = 2;
int TYPE_SYMBOL = 3;
int TYPE_LETTER_AND_NUM = 4;
int TYPE_NUM_AND_SYMBOL = 5;
}
}
@KeyboardType
@IntDef(
value = [KEYBOARD_TYPE.TYPE_LETTER,
KEYBOARD_TYPE.TYPE_NUMBER,
KEYBOARD_TYPE.TYPE_SYMBOL,
KEYBOARD_TYPE.TYPE_LETTER_AND_NUM,
KEYBOARD_TYPE.TYPE_NUM_AND_SYMBOL]
)
@Retention(AnnotationRetention.SOURCE)
annotation class KeyboardType
random_keyboard_viewgroup.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/random_keyboard_group"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.core.widget.NestedScrollView
android:id="@+id/sl_content"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<com.*.widget.RandomKeyboard
android:id="@+id/random_keyboard"
style="@style/RandomKeyboard"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
styles.xml
<style name="RandomKeyboardParent">
<item name="android:background">#212121</item>
<item name="android:keyBackground">@drawable/bg_keyboard</item>
<item name="android:keyPreviewHeight">50dp</item>
<item name="android:keyPreviewLayout">@layout/keyboard_preview</item>
<item name="android:keyPreviewOffset">-20dp</item>
<item name="android:keyTextColor">#dddddd</item>
<item name="android:keyTextSize">18sp</item>
<item name="android:labelTextSize">18sp</item>
<item name="android:paddingTop">2dp</item>
<item name="android:paddingBottom">2dp</item>
</style>
<style name="RandomKeyboard" parent="RandomKeyboardParent" />
attrs.xml
<attr name="isRandom" format="boolean" />
<attr name="keyboardType" format="enum">
<enum name="TYPE_LETTER" value="1" />
<enum name="TYPE_NUMBER" value="2" />
<enum name="TYPE_SYMBOL" value="3" />
<enum name="TYPE_LETTER_AND_NUM" value="4" />
<enum name="TYPE_NUM_AND_SYMBOL" value="5" />
</attr>
<declare-styleable name="RandomKeyboard">
<attr name="isRandom"/>
<attr name="keyboardType"/>
</declare-styleable>
<declare-styleable name="RandomKeyboardViewGroup">
<attr name="isRandom"/>
<attr name="keyboardType"/>
</declare-styleable>
keyboard_preview.xml
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/bg_key_preview"
android:gravity="center"
android:padding="5dp"
android:textColor="#4D4D4D"
android:textSize="20sp"
android:textStyle="bold"/>
bg_key_preview.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="4dp" />
<solid android:color="@color/subColorWhite" />
<stroke
android:width="1px"
android:color="@color/gray" />
</shape>
bg_keyboard.xml
<?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>
letter_keyboard_layout.xml
<?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: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:keyLabel="shift"
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:keyLabel="delete"
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="64"
android:keyLabel="\@"/>
<Key
android:codes="32"
android:isRepeatable="true"
android:keyWidth="35%p"
android:keyLabel="space" />
<Key android:keyLabel="." />
<Key
android:codes="-12"
android:keyWidth="15%p"
android:keyLabel="!?#" />
<Key
android:codes="-13"
android:keyWidth="15%p"
android:keyEdgeFlags="right"
android:keyLabel="完成" />
</Row>
</Keyboard>
letter_num_keyboard_layout.xml
<?xml version="1.0" encoding="UTF-8"?>
<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
android:keyWidth="10%p"
android:keyHeight="50dp">
<Row android:rowEdgeFlags="top">
<Key
android:keyEdgeFlags="left"
android:keyLabel="1" />
<Key android:keyLabel="2" />
<Key android:keyLabel="3" />
<Key android:keyLabel="4" />
<Key android:keyLabel="5" />
<Key android:keyLabel="6" />
<Key android:keyLabel="7" />
<Key android:keyLabel="8" />
<Key android:keyLabel="9" />
<Key android:keyEdgeFlags="right"
android:keyLabel="0" />
</Row>
<Row>
<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: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:keyWidth="15%p"
android:keyHeight="100dp"
android:keyEdgeFlags="left"
android:keyLabel="shift" />
<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:keyWidth="15%p"
android:keyEdgeFlags="right"
android:keyLabel="delete" />
</Row>
<Row android:rowEdgeFlags="bottom">
<Key
android:codes="64"
android:horizontalGap="15%p"
android:keyWidth="15%p"
android:keyLabel="\@"/>
<Key
android:codes="32"
android:isRepeatable="true"
android:keyWidth="40%p"
android:keyLabel="space" />
<Key
android:codes="-12"
android:keyWidth="15%p"
android:keyLabel="!?#" />
<Key
android:codes="-13"
android:isRepeatable="true"
android:keyWidth="15%p"
android:keyEdgeFlags="right"
android:keyLabel="完成" />
</Row>
</Keyboard>
num_symbol_keyboard_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
android:horizontalGap="0.9%p"
android:keyWidth="9%p"
android:keyHeight="52dp"
android:verticalGap="0px">
<Row>
<Key
android:codes="49"
android:keyLabel="1" />
<Key
android:codes="50"
android:keyLabel="2" />
<Key
android:codes="51"
android:keyLabel="3" />
<Key
android:codes="52"
android:keyLabel="4" />
<Key
android:codes="53"
android:keyLabel="5" />
<Key
android:codes="54"
android:keyLabel="6" />
<Key
android:codes="55"
android:keyLabel="7" />
<Key
android:codes="56"
android:keyLabel="8" />
<Key
android:codes="57"
android:keyLabel="9" />
<Key
android:codes="48"
android:keyLabel="0" />
</Row>
<Row>
<Key
android:codes="45"
android:keyLabel="-" />
<Key
android:codes="47"
android:keyLabel="/" />
<Key
android:codes="58"
android:keyLabel=":" />
<Key
android:codes="59"
android:keyLabel=";" />
<Key
android:codes="40"
android:keyLabel="(" />
<Key
android:codes="41"
android:keyLabel=")" />
<Key
android:codes="36"
android:keyLabel="$" />
<!--&-->
<Key
android:codes="38"
android:keyLabel="&" />
<Key
android:codes="64"
android:keyLabel="\@" />
<Key
android:codes="34"
android:keyLabel=""" />
</Row>
<Row>
<Key
android:codes="-12"
android:keyWidth="11.5%p"
android:keyEdgeFlags="left"
android:keyLabel="!?#" />
<Key
android:codes="46"
android:keyLabel="." />
<Key
android:codes="44"
android:keyLabel="," />
<Key
android:codes="63"
android:keyLabel="\?" />
<Key
android:codes="33"
android:keyLabel="!" />
<Key
android:codes="39"
android:keyLabel="'" />
<Key
android:codes="42"
android:keyLabel="*" />
<Key
android:codes="43"
android:keyLabel="+" />
<Key
android:codes="-5"
android:isRepeatable="true"
android:keyWidth="16.5%p"
android:keyEdgeFlags="right"
android:keyLabel="delete" />
</Row>
<Row android:rowEdgeFlags="bottom">
<Key
android:codes="-10"
android:keyWidth="22%p"
android:keyLabel="ABC" />
<Key
android:codes="32"
android:isRepeatable="true"
android:keyWidth="50.4%p"
android:keyLabel="space" />
<Key
android:codes="-13"
android:keyWidth="24%p"
android:keyEdgeFlags="right"
android:keyLabel="完成" />
</Row>
</Keyboard>
number_keyboard_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
android:keyWidth="25%p"
android:keyHeight="50dp">
<Row>
<Key
android:keyEdgeFlags="left"
android:keyLabel="1" />
<Key android:keyLabel="2" />
<Key android:keyLabel="3" />
<Key
android:codes="-5"
android:isModifier="true"
android:isRepeatable="true"
android:keyEdgeFlags="right"
android:keyLabel="delete" />
</Row>
<Row>
<Key
android:keyEdgeFlags="left"
android:keyLabel="4" />
<Key android:keyLabel="5" />
<Key android:keyLabel="6" />
<Key
android:codes="-12"
android:keyEdgeFlags="right"
android:keyLabel="!?#" />
</Row>
<Row>
<Key
android:keyEdgeFlags="left"
android:keyLabel="7" />
<Key android:keyLabel="8" />
<Key android:keyLabel="9" />
<Key
android:keyEdgeFlags="right"
android:keyLabel="." />
</Row>
<Row android:rowEdgeFlags="bottom">
<Key
android:codes="-10"
android:keyWidth="25%p"
android:keyEdgeFlags="left"
android:keyLabel="ABC" />
<Key
android:codes="48"
android:keyWidth="50%p"
android:keyLabel="0" />
<Key
android:codes="-13"
android:keyWidth="25%p"
android:keyEdgeFlags="right"
android:keyLabel="完成" />
</Row>
</Keyboard>
symbol_keyboard_layout.xml
<?xml version="1.0" encoding="UTF-8"?>
<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
android:keyHeight="52dp"
android:keyWidth="9%p"
android:horizontalGap="0.9%p"
android:verticalGap="0px">
<Row>
<Key
android:codes="91"
android:keyEdgeFlags="left"
android:keyLabel="["/>
<Key
android:codes="93"
android:keyLabel="]"/>
<Key
android:codes="123"
android:keyLabel="{"/>
<Key
android:codes="125"
android:keyLabel="}"/>
<Key
android:codes="35"
android:keyLabel="#"/>
<Key
android:codes="37"
android:keyLabel="%"/>
<Key
android:codes="94"
android:keyLabel="^"/>
<Key
android:codes="42"
android:keyLabel="*"/>
<Key
android:codes="43"
android:keyLabel="+"/>
<Key
android:codes="61"
android:keyEdgeFlags="right"
android:keyLabel="="/>
</Row>
<Row>
<Key
android:codes="95"
android:keyEdgeFlags="left"
android:keyLabel="_"/>
<Key
android:codes="92"
android:keyLabel="\\"/>
<Key
android:codes="124"
android:keyLabel="|"/>
<Key
android:codes="126"
android:keyLabel="~"/>
<!--<-->
<Key
android:codes="60"
android:keyLabel="<"/>
<!-->-->
<Key
android:codes="62"
android:keyLabel=">"/>
<Key
android:codes="8364"
android:keyLabel="€"/>
<Key
android:codes="163"
android:keyLabel="£"/>
<Key
android:codes="165"
android:keyLabel="¥"/>
<!-- todo • -->
<Key
android:codes="8226"
android:keyLabel="•"/>
</Row>
<Row>
<Key
android:codes="-11"
android:keyEdgeFlags="left"
android:keyLabel="123"
android:keyWidth="16.9%p" />
<Key
android:codes="45"
android:keyLabel="-" />
<Key
android:codes="46"
android:keyLabel="." />
<Key
android:codes="44"
android:keyLabel="," />
<Key
android:codes="63"
android:keyLabel="\?" />
<Key
android:codes="33"
android:keyLabel="!" />
<Key
android:codes="39"
android:keyLabel="'" />
<Key
android:codes="-5"
android:isRepeatable="true"
android:keyWidth="21%p"
android:keyEdgeFlags="right"
android:keyLabel="delete" />
</Row>
<Row android:rowEdgeFlags="bottom">
<Key
android:codes="-10"
android:keyWidth="22%p"
android:keyLabel="ABC" />
<Key
android:codes="32"
android:isRepeatable="true"
android:keyWidth="50.4%p"
android:keyLabel="space" />
<Key
android:codes="-13"
android:keyWidth="24%p"
android:keyEdgeFlags="right"
android:keyLabel="完成" />
</Row>
</Keyboard>
附(KeyboardView,keyboard,Key,Row属性解释)
参数
值类型
释义
传值
keyHeight
dimension/fractional
key高度
精确值(dp、px等)和相对值(%、%p)
keyWidth
dimension/fractional
key宽度
同上
horizontalGap
dimension/fractional
key间水平间隙
同上
verticalGap
dimension/fractional
key间垂直间隙
同上
参数
值类型
释义
传值
keyHeight
dimension/fractional
key高度
精确值(dp、px等)和相对值(%、%p)
keyWidth
dimension/fractional
key宽度
同上
horizontalGap
dimension/fractional
key间水平间隙
同上
verticalGap
dimension/fractional
key间垂直间隙
同上
keyboardMode
reference
键盘模式,不符该模式则跳过此行
rowEdgeFlags
enum
行边界标记
top/bottom,键盘顶部和底部
参数
值类型
释义
传值
keyHeight
dimension/fractional
key高度
精确值(dp、px等)和相对值(%、%p)
keyWidth
dimension/fractional
key宽度
同上
horizontalGap
dimension/fractional
key间水平间隙
同上
verticalGap
dimension/fractional
key间垂直间隙
同上
keyEdgeFlags
enum
Key边缘位置标记
left/right,键盘左边和右边
codes
int
Key输出符号对应的Unicode值
iconPreview
reference
弹出回显的控件
布局资源
isModifier
boolean
是否为功能键,如:Alt/Shift
isRepeatable
boolean
是否允许长按重复执行
isSticky
boolean
是否是开关键,例如Shift大小写切换按键,具有两种状态,按下状态和正常状态,取值为true或则false。
keyIcon
reference
替换label显示在按键上的图标
图片资源
keyLabel
reference
显示在Key上的标签
字符
keyOutputText
string
Key按下时输出的字符或字符串
popupCharacters
string
小键盘显示的字符,用于显示Key候选项。
popupKeyboard
reference
按键候选小键盘的keyboard布局
布局资源
参数
值类型
释义
传值
keyBackground
reference
按键的图像背景,必须包含多个状态的drawable
verticalCorrection
dimension
补充触摸y座标的偏移,用于偏差矫正
keyPreviewLayout
reference
按键按下时预览框的布局
keyPreviewOffset
dimension
按键按下时预览框的偏移。>0 向下,<0 向上。
keyPreviewHeight
dimension
按键按下时预览框的高度。
keyTextSize
dimension
按键文字大小。
keyTextColor
color
按键文字颜色。
labelTextSize
dimension
标签文字大小,keylabel有多个字符且keycodes只有一个值时,该属性生效。
popupLayout
reference
按键候选小键盘的KeyboardView布局。
shadowRadius
float
按键文字阴影半径
shadowColor
color
按键文字阴影颜色