實現思路:
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
按鍵文字陰影顏色