在银行APP里经常要自定义键盘,例如实现下面这样的效果
首先在xml文件里定义键盘
代码语言:javascript复制<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
android:keyWidth="30%p"
android:horizontalGap="@dimen/keyboard_horizontalGap"
android:verticalGap="@dimen/keyboard_verticalGap"
android:keyHeight="47dp">
<Row>
<Key android:codes="49" android:keyLabel="1" />
<Key android:codes="50" android:keyLabel="2" />
<Key android:codes="51" android:keyLabel="3" />
</Row>
<Row>
<Key android:codes="52" android:keyLabel="4" />
<Key android:codes="53" android:keyLabel="5" />
<Key android:codes="54" android:keyLabel="6" />
</Row>
<Row>
<Key android:codes="55" android:keyLabel="7" />
<Key android:codes="56" android:keyLabel="8" />
<Key android:codes="57" android:keyLabel="9" />
</Row>
<Row>
<Key android:codes="-2" android:keyLabel="ABC" />
<Key android:codes="48" android:keyLabel="0" />
<Key android:codes="-35"
android:isRepeatable="true"/>
</Row>
</Keyboard>
keyWidth:每一个按钮的宽度
keyHeight:每一个按钮高度,可以设置百分比
horizontalGap:水平间隔
verticalGap:竖直间隔
Row:一行
每一个按键都将会有一个 codes 值,代表键盘上的按键
代码语言:javascript复制KhKeyboardView
代码语言:javascript复制public class KhKeyboardView {
private Activity mContext;
private View parentView;
private KeyboardView mLetterView; //字母键盘view
private KeyboardView mNumberView; //数字键盘View
private Keyboard mNumberKeyboard; // 数字键盘
private Keyboard mLetterKeyboard; // 字母键盘
private Keyboard mSymbolKeyboard; // 符号键盘
private boolean isNumber = true; // 是否数字键盘
public static boolean isUpper = false; // 是否大写
private boolean isSymbol = false; // 是否是符号
private EditText mEditText;
private View headerView;
public void setEditText(EditText text) {
mEditText = text;
}
public KhKeyboardView(Activity context, View view) {
mContext = context;
parentView = view;
mNumberKeyboard = new Keyboard(mContext, R.xml.keyboard_numbers);
mLetterKeyboard = new Keyboard(mContext, R.xml.keyboard_word);
mSymbolKeyboard = new Keyboard(mContext, R.xml.keyboard_symbol);
mNumberView = (KeyboardView) parentView.findViewById(R.id.keyboard_view);
mLetterView = (KeyboardView) parentView.findViewById(R.id.keyboard_view_2);
mNumberView.setKeyboard(mNumberKeyboard);
mNumberView.setEnabled(true);
mNumberView.setPreviewEnabled(false);
mNumberView.setOnKeyboardActionListener(listener);
mLetterView.setKeyboard(mLetterKeyboard);
mLetterView.setEnabled(true);
mLetterView.setPreviewEnabled(true);
mLetterView.setOnKeyboardActionListener(listener);
headerView = parentView.findViewById(R.id.keyboard_header);
}
private KeyboardView.OnKeyboardActionListener listener = new KeyboardView.OnKeyboardActionListener() {
/**
* 按下,在onKey之前,可以在这里做一些操作,这里让有的没有按下的悬浮提示
* @param primaryCode
*/
@Override
public void onPress(int primaryCode) {
Log.d("primaryCode","onPress--" primaryCode);
if (primaryCode == Keyboard.KEYCODE_SHIFT) {
mLetterView.setPreviewEnabled(false);
} else if (primaryCode == Keyboard.KEYCODE_DELETE) {
mLetterView.setPreviewEnabled(false);
} else if (primaryCode == 32) {
mLetterView.setPreviewEnabled(false);
} else {
mLetterView.setPreviewEnabled(true);
}
}
/**
* 松开
* @param primaryCode
*/
@Override
public void onRelease(int primaryCode) {
Log.d("primaryCode","onRelease--" primaryCode);
}
/**
* 按下
* @param primaryCode
* @param keyCodes
*/
@Override
public void onKey(int primaryCode, int[] keyCodes) {
Log.d("primaryCode","onKey--" primaryCode);
try {
if (mEditText == null)
return;
Editable editable = mEditText.getText();
int start = mEditText.getSelectionStart();
if (primaryCode == Keyboard.KEYCODE_CANCEL) {
// 隐藏键盘
hideKeyboard();
} else if (primaryCode == Keyboard.KEYCODE_DELETE || primaryCode == -35) {
// 回退键,删除字符
if (editable != null && editable.length() > 0) {
if (start > 0) {
editable.delete(start - 1, start);
}
}
} else if (primaryCode == Keyboard.KEYCODE_SHIFT) {
// 大小写切换
changeKeyboart();
mLetterView.setKeyboard(mLetterKeyboard);
} else if (primaryCode == Keyboard.KEYCODE_MODE_CHANGE) {
// 数字与字母键盘互换
if (isNumber) {
showLetterView();
showLetterView2();
} else {
showNumberView();
}
} else if (primaryCode == 90001) {
// 字母与符号切换
if (isSymbol) {
showLetterView2();
} else {
showSymbolView();
}
} else {
// 输入键盘值
editable.insert(start, Character.toString((char) primaryCode));
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onText(CharSequence text) {
}
@Override
public void swipeLeft() {
}
@Override
public void swipeRight() {
}
@Override
public void swipeDown() {
}
@Override
public void swipeUp() {
}
};
// 字母-符号,显示字母
private void showLetterView2() {
if (mLetterView != null) {
isSymbol = false;
mLetterView.setKeyboard(mLetterKeyboard);
}
}
// 字母-符号,显示符号
private void showSymbolView() {
try {
if (mLetterKeyboard != null) {
isSymbol = true;
mLetterView.setKeyboard(mSymbolKeyboard);
}
} catch (Exception e) {
}
}
// 数字-字母,显示字母键盘
private void showLetterView() {
try {
if (mLetterView != null && mNumberView != null) {
isNumber = false;
mLetterView.setVisibility(View.VISIBLE);
mNumberView.setVisibility(View.INVISIBLE);
}
} catch (Exception e) {
e.printStackTrace();
}
}
// 数字-字母, 显示数字键盘
private void showNumberView() {
try {
if (mLetterView != null && mNumberView != null) {
isNumber = true;
mLetterView.setVisibility(View.INVISIBLE);
mNumberView.setVisibility(View.VISIBLE);
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 切换大小写
*/
private void changeKeyboart() {
List<Keyboard.Key> keyList = mLetterKeyboard.getKeys();
if (isUpper) {
// 大写切换小写
isUpper = false;
for (Keyboard.Key key : keyList) {
Drawable icon = key.icon;
if (key.label != null && isLetter(key.label.toString())) {
key.label = key.label.toString().toLowerCase();
key.codes[0] = key.codes[0] 32;
}
}
} else {
// 小写切换成大写
isUpper = true;
for (Keyboard.Key key : keyList) {
if (key.label != null && isLetter(key.label.toString())) {
key.label = key.label.toString().toUpperCase();
key.codes[0] = key.codes[0] - 32;
}
}
}
}
/**
* 判断是否是字母
*/
private boolean isLetter(String str) {
String wordStr = "abcdefghijklmnopqrstuvwxyz";
return wordStr.contains(str.toLowerCase());
}
public void hideKeyboard() {
try {
int visibility = mLetterView.getVisibility();
if (visibility == View.VISIBLE) {
headerView.setVisibility(View.GONE);
mLetterView.setVisibility(View.GONE);
}
visibility = mNumberView.getVisibility();
if (visibility == View.VISIBLE) {
headerView.setVisibility(View.GONE);
mNumberView.setVisibility(View.GONE);
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 显示键盘
*
* @param editText
*/
public void showKeyboard(EditText editText) {
try {
this.mEditText = editText;
int visibility = 0;
int inputText = mEditText.getInputType();
headerView.setVisibility(View.VISIBLE);
switch (inputText) {
case InputType.TYPE_CLASS_NUMBER:
showNumberView();
break;
case InputType.TYPE_CLASS_PHONE:
showNumberView();
break;
case InputType.TYPE_NUMBER_FLAG_DECIMAL:
showNumberView();
break;
default:
showLetterView();
break;
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
布局
代码语言:javascript复制<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@ id/rl_key"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#00000000"
android:orientation="vertical">
<View
android:id="@ id/keyboard_back_hide"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#7d7d7d"
android:orientation="vertical">
<RelativeLayout
android:id="@ id/keyboard_header"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="visible">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="智能安全加密键盘"
android:textColor="#bfbfbf"
android:textSize="15sp" />
<TextView
android:id="@ id/keyboard_finish"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginRight="10dp"
android:padding="14dp"
android:text="完成"
android:textColor="#ffffff"
android:textSize="15sp" />
</RelativeLayout>
<ImageView
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginBottom="10dp"
android:background="#555457" />
<FrameLayout
android:id="@ id/keyboard_layer"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.kh.keyboard.CustomKeyboardView
android:id="@ id/keyboard_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#7d7d7d"
android:focusable="true"
android:focusableInTouchMode="true"
android:keyBackground="@drawable/keyboard_number_selector_bg"
android:keyPreviewLayout="@null"
android:keyTextColor="#ffffff"
android:visibility="gone" />
<com.kh.keyboard.CustomKeyboardView
android:id="@ id/keyboard_view_2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#7d7d7d"
android:focusable="true"
android:focusableInTouchMode="true"
android:keyBackground="@drawable/keyboard_selector_bg"
android:keyPreviewHeight="90dp"
android:keyPreviewLayout="@layout/keyboard_key_preview_layout"
android:keyPreviewOffset="45dp"
android:keyTextColor="#ffffff"
android:visibility="gone" />
</FrameLayout>
</LinearLayout>
</RelativeLayout>
keyPreviewLayout就是点击时键盘按键上的悬浮效果
这里自定义了KeyboardView,因为我需要按钮的背景颜色不一样,而使用keyBackground都是一样的
代码语言:javascript复制public class CustomKeyboardView extends KeyboardView {
private Context context;
public CustomKeyboardView(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
}
@Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
try {
List<Keyboard.Key> keys = getKeyboard().getKeys();
for (Keyboard.Key key : keys) {
// Log.e("KEY", "Drawing key with code " key.codes[0]);
if (key.codes[0] == -5) {
Drawable dr = (Drawable) context.getResources().getDrawable(R.drawable.keyboard_word_del_layerlist);
dr.setBounds(key.x, key.y, key.x key.width, key.y key.height);
dr.draw(canvas);
} else if (key.codes[0] == -35) {
Drawable dr = (Drawable) context.getResources().getDrawable(R.drawable.keyboard_word_del_layerlist2);
dr.setBounds(key.x, key.y, key.x key.width, key.y key.height);
dr.draw(canvas);
} else if (key.codes[0] == -1) {
Drawable dr = (Drawable) context.getResources().getDrawable(R.drawable.keyboard_word_shift_layerlist);
Drawable dr_da = (Drawable) context.getResources().getDrawable(R.drawable.keyboard_word_shift_layerlist_da);
dr.setBounds(key.x, key.y, key.x key.width, key.y key.height);
dr_da.setBounds(key.x, key.y, key.x key.width, key.y key.height);
if (KhKeyboardView.isUpper) {
dr_da.draw(canvas);
} else {
dr.draw(canvas);
}
} else if (key.codes[0] == -2 || key.codes[0] == 90001) {
Drawable dr = (Drawable) context.getResources().getDrawable(R.drawable.keyboard_selector_blue_bg);
dr.setBounds(key.x, key.y, key.x key.width, key.y key.height);
dr.draw(canvas);
drawText(canvas, key);
} else {
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
private void drawText(Canvas canvas, Keyboard.Key key) {
try {
Rect bounds = new Rect();
Paint paint = new Paint();
paint.setTextAlign(Paint.Align.CENTER);
paint.setAntiAlias(true);
paint.setColor(Color.WHITE);
if (key.label != null) {
String label = key.label.toString();
Field field;
if (label.length() > 1 && key.codes.length < 2) {
int labelTextSize = 0;
try {
field = KeyboardView.class.getDeclaredField("mLabelTextSize");
field.setAccessible(true);
labelTextSize = (int) field.get(this);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
paint.setTextSize(labelTextSize);
paint.setTypeface(Typeface.DEFAULT_BOLD);
} else {
int keyTextSize = 0;
try {
field = KeyboardView.class.getDeclaredField("mLabelTextSize");
field.setAccessible(true);
keyTextSize = (int) field.get(this);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
paint.setTextSize(keyTextSize);
paint.setTypeface(Typeface.DEFAULT);
}
paint.getTextBounds(key.label.toString(), 0, key.label.toString()
.length(), bounds);
canvas.drawText(key.label.toString(), key.x (key.width / 2),
(key.y key.height / 2) bounds.height() / 2, paint);
} else if (key.icon != null) {
key.icon.setBounds(key.x (key.width - key.icon.getIntrinsicWidth()) / 2, key.y (key.height - key.icon.getIntrinsicHeight()) / 2,
key.x (key.width - key.icon.getIntrinsicWidth()) / 2 key.icon.getIntrinsicWidth(), key.y (key.height - key.icon.getIntrinsicHeight()) / 2 key.icon.getIntrinsicHeight());
key.icon.draw(canvas);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
最后
还需要一个工具类来显示自定义的键盘,这里我使用了dialog
代码语言:javascript复制public class KeyBoardDialogUtils implements View.OnClickListener {
protected View view;
protected Dialog popWindow;
protected Activity mContext;
private EditText contentView;
private List<String> contentList;
private KhKeyboardView keyboardUtil;
public KeyBoardDialogUtils(Activity mContext) {
try {
this.mContext = mContext;
if (contentList == null) {
contentList = new ArrayList<>();
}
if (popWindow == null) {
view = LayoutInflater.from(mContext).inflate(R.layout.keyboard_key_board_popu, null);
view.findViewById(R.id.keyboard_finish).setOnClickListener(this);
view.findViewById(R.id.keyboard_back_hide).setOnClickListener(this);
}
popWindow.setContentView(view);
popWindow.setCanceledOnTouchOutside(true);
Window mWindow = popWindow.getWindow();
mWindow.setWindowAnimations(R.style.keyboard_popupAnimation);
mWindow.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
mWindow.setGravity(Gravity.BOTTOM | Gravity.FILL_HORIZONTAL);
mWindow.setLayout(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
popWindow.setOnDismissListener(new DialogInterface.OnDismissListener() {
@Override
public void onDismiss(DialogInterface dialog) {
if (contentView != null && contentView.isFocused()) {
contentView.clearFocus();
}
}
});
initView();
} catch (Exception e) {
e.printStackTrace();
}
}
private void initView() {
try {
if (keyboardUtil == null)
keyboardUtil = new KhKeyboardView(mContext, view);
} catch (Exception e) {
e.printStackTrace();
}
}
public void show(final EditText editText) {
editText.setFocusable(true);
editText.setFocusableInTouchMode(true);
editText.requestFocus();
popWindow.show();
keyboardUtil.showKeyboard(editText);
}
public void dismiss() {
if (popWindow != null && popWindow.isShowing()) {
popWindow.dismiss();
}
}
@Override
public void onClick(View v) {
try {
int i = v.getId();
if (i == R.id.keyboard_finish) {
keyboardUtil.hideKeyboard();
dismiss();
} else if (i == R.id.keyboard_back_hide) {
keyboardUtil.hideKeyboard();
dismiss();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
使用
代码语言:javascript复制 et = (EditText) findViewById(R.id.et);
keyBoardDialogUtils = new KeyBoardDialogUtils(this);
et.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
keyBoardDialogUtils.show(et);
}
});
注意这里点击会先弹出系统键盘,因为弹出键盘会先于keyBoardDialogUtils.show(et)执行,所以设置EditText的focusableInTouchMode="false",在keyutil里我们再把它设为true。
项目源码:
https://github.com/peiniwan/SafeKeyBoard