自定义软键盘-屏蔽系统输入法,随机键盘
- 核心代码
- RandomKeyboard
- RandomKeyboardViewGroup
- 自定义RandomKeyboard
- 自定义keyboardViewGroup
- 其他资源
- OnCustomKeyDealListener
- OnKeyboardToggleListener
- KeyModel.class
- KEYBOARD_TYPE
- @KeyboardType
- random_keyboard_viewgroup.xml
- styles.xml
- attrs.xml
- keyboard_preview.xml
- bg_key_preview.xml
- bg_keyboard.xml
- letter_keyboard_layout.xml
- letter_num_keyboard_layout.xml
- num_symbol_keyboard_layout.xml
- number_keyboard_layout.xml
- symbol_keyboard_layout.xml
- 附(KeyboardView,keyboard,Key,Row属性解释)
- 相关
实现思路:
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 (customKeyDealListener == null || !customKeyDealListener.onKey(editText, primaryCode, keyCodes)) {
if (primaryCode >= 97 && primaryCode <= 97 + 26) {
if (editText != null && editable != 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;
}
}
}
- 随机打乱key
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();
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;
}
- 禁用系统输入法
//禁用系统输入法
if (context instanceof Activity) {
((Activity) context).getWindow().addFlags(FLAG_ALT_FOCUSABLE_IM);
} else {
Activity currentActivity = AppManager.getAppManager().getCurrentActivity();
if (currentActivity != null) {
currentActivity.getWindow().addFlags(FLAG_ALT_FOCUSABLE_IM);
}
}
RandomKeyboardViewGroup
- 自动查找绑定EditText
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
boolean touchEvent = super.dispatchTouchEvent(ev);
View editFocus = findEditFocus();
if (editFocus != null && editFocus.hasFocus()) {
keyboard.bindEditText((EditText) editFocus);
keyboard.show();
} else {
keyboard.hide();
}
if (ViewUtils.isOutsideTheView(editFocus, ev) && ViewUtils.isOutsideTheView(keyboard, ev)) {
editFocus.clearFocus();
}
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;
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<>();
private OnKeyboardToggleListener toggleListener;
private OnCustomKeyDealListener customKeyDealListener;
@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);
keyType = typedArray.getInt(R.styleable.RandomKeyboard_keyboardType, TYPE_LETTER);
int resourceId = typedArray.getResourceId(R.styleable.RandomKeyboard_android_keyboardLayout, 0);
typedArray.recycle();
setCustomKeyboard(resourceId);
setOnKeyboardActionListener(this);
hide();
//禁用系统输入法
if (context instanceof Activity) {
((Activity) context).getWindow().addFlags(FLAG_ALT_FOCUSABLE_IM);
} else {
Activity currentActivity = AppManager.getAppManager().getCurrentActivity();
if (currentActivity != null) {
currentActivity.getWindow().addFlags(FLAG_ALT_FOCUSABLE_IM);
}
}
}
/**
* 绑定EditText
*/
public RandomKeyboard bindEditText(EditText editText) {
this.editText = editText;
if (editText != null) {
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;
}
public RandomKeyboard setCustomKeyboard(@XmlRes int xml) {
try {
context.getResources().getXml(xml);
keyboard = new Keyboard(context, xml);
setKeyboard(keyboard);
keyType = TYPE_CUSTOM;
isRandom(isNeedRandom);
} catch (Resources.NotFoundException e) {
setKeyboard(keyType);
}
return this;
}
/**
* @see com.quanyou.libraryview.common.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 (customKeyDealListener == null || !customKeyDealListener.onKey(editText, primaryCode, keyCodes)) {
if (primaryCode >= 97 && primaryCode <= 97 + 26) {
if (editText != null && editable != 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();
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);
if (toggleListener != null) {
toggleListener.onToggle(true);
}
}
return this;
}
public RandomKeyboard hide() {
if (getVisibility() != GONE || getVisibility() != INVISIBLE) {
setVisibility(GONE);
if (toggleListener != null) {
toggleListener.onToggle(false);
}
}
return this;
}
public void setOnKeyboardToggleListener(OnKeyboardToggleListener toggleListener) {
this.toggleListener = toggleListener;
}
public RandomKeyboard setOnCustomKeyDealListener(OnCustomKeyDealListener customKeyDealListener) {
this.customKeyDealListener = customKeyDealListener;
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);
int resourceId = typedArray.getResourceId(R.styleable.RandomKeyboard_android_keyboardLayout, 0);
typedArray.recycle();
setFocusable(true);
setFocusableInTouchMode(true);
try {
context.getResources().getXml(resourceId);
keyboard.setCustomKeyboard(resourceId);
} catch (Resources.NotFoundException e) {
setKeyboard(anInt);
}
isRandom(aBoolean);
}
@Override
public void onWindowFocusChanged(boolean hasWindowFocus) {
super.onWindowFocusChanged(hasWindowFocus);
View editFocus = findEditFocus();
if (keyboard != null) {
if (editFocus != null) {
keyboard.bindEditText((EditText) editFocus);
keyboard.show();
} else {
keyboard.hide();
}
}
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
boolean touchEvent = super.dispatchTouchEvent(ev);
View editFocus = findEditFocus();
if (editFocus != null && editFocus.hasFocus()) {
keyboard.bindEditText((EditText) editFocus);
keyboard.show();
} else {
keyboard.hide();
}
if (ViewUtils.isOutsideTheView(editFocus, ev) && ViewUtils.isOutsideTheView(keyboard, ev)) {
editFocus.clearFocus();
}
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 void setCustomKeyboard(@XmlRes int xml) {
if (keyboard != null) {
keyboard.setCustomKeyboard(xml);
}
}
public void setOnCustomKeyDealListener(OnCustomKeyDealListener customKeyDealListener) {
if (keyboard != null) {
keyboard.setOnCustomKeyDealListener(customKeyDealListener);
}
}
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);
}
public void setOnKeyboardToggleListener(OnKeyboardToggleListener toggleListener) {
if (keyboard != null) {
keyboard.setOnKeyboardToggleListener(toggleListener);
}
}
}
其他资源
OnCustomKeyDealListener
public interface OnCustomKeyDealListener {
boolean onKey(EditText editText, int primaryCode, int[] keyCodes);
}
OnKeyboardToggleListener
public interface OnKeyboardToggleListener {
void onToggle(boolean isOpen);
}
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" />
<attr name="android:keyboardLayout"/>
</declare-styleable>
<declare-styleable name="RandomKeyboardViewGroup">
<attr name="isRandom" />
<attr name="keyboardType" />
<attr name="android:keyboardLayout"/>
</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属性解释)
- Keyboard
参数 | 值类型 | 释义 | 传值 |
keyHeight | dimension/fractional | key高度 | 精确值(dp、px等)和相对值(%、%p) |
keyWidth | dimension/fractional | key宽度 | 同上 |
horizontalGap | dimension/fractional | key间水平间隙 | 同上 |
verticalGap | dimension/fractional | key间垂直间隙 | 同上 |
- Row
参数 | 值类型 | 释义 | 传值 |
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,键盘顶部和底部 |
- Key
参数 | 值类型 | 释义 | 传值 |
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布局 | 布局资源 |
- KeyboardView
参数 | 值类型 | 释义 | 传值 |
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 | 按键文字阴影颜色 |