【记录】记录点滴
场景: 实现自定义的身份证键盘
需求: 实现0-9数字键,X键,确认键(或其他),删除键
1. 利用Keyboard实现布局
在res/xml目录下创建keyboard的布局xml文件,如下:
/** 系统定义的值 */
public static final int EDGE_LEFT = 1;
public static final int EDGE_RIGHT = 2;
public static final int EDGE_TOP = 4;
public static final int EDGE_BOTTOM = 8;
public static final int KEYCODE_SHIFT = -1;
public static final int KEYCODE_MODE_CHANGE = -2;
public static final int KEYCODE_CANCEL = -3;
public static final int KEYCODE_DONE = -4;
public static final int KEYCODE_DELETE = -5
public static final int KEYCODE_ALT = -6;
简单使用的话,只需要在布局文件中添加KeyboardView,并在代码中将它和上述的Keyboard布局关联起来,例如
//Java代码
Keyboard kb= new Keyboard(context, R.xml.keyboard);
kbView.setKeyboard(kb);
keyboardView.setEnabled(true);
keyboardView.setPreviewEnabled(false);
keyboardView.setOnKeyboardActionListener(new KeyboardView.OnKeyboardActionListener({
...
...
});
根据需求,在KeyboardView.OnKeyboardActionListener中实现onKey等
2. 基于KeyboardView实现自定义样式,如默认按键样式/点击效果
在我的场景中,需要将确认按钮的背景色设置为其他颜色,删除按钮用图案,按键实现按压效果,所以需要自定义样式
创建CustomKeyboardView继承KeyboardView,根据按键内容,状态绘制对应的按键
/** 只展示关键代码 */
public class CustomKeyboardView extends KeyboardView {
@Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
Keyboard keyboard = getKeyboard();
if(keyboard == null){
return;
}
//获得全部按键
List keys = keyboard.getKeys();
if(keys != null && keys.size() > 0){
for(Keyboard.Key key : keys){
if(key.codes[0] == Keyboard.KEYCODE_DELETE){
//删除按键,绘制图案
Drawable drawable = getContext().getResources().getDrawable(R.drawable.common_keyboard_delete);
if(key.pressed){
//按压时
drawable.setAlpha(100);
} else {
drawable.setAlpha(255);
}
//根据key的坐标,计算图案的绘制位置
drawable.setBounds(key.x + 2*key.width/5, key.y + 2*key.height/5, (int) (key.x + key.width - 2.1f*key.width/5), (int) (key.y + key.height - 2.1f*key.height/5));
drawable.draw(canvas);
} else if(key.codes[0] == Keyboard.KEYCODE_DONE){
//确认按键
Drawable drawable;
if(key.pressed){
drawable = getContext().getResources().getDrawable(R.drawable.common_shape_id_keyboard_check_pressed);
} else {
drawable = getContext().getResources().getDrawable(R.drawable.common_shape_id_keyboard_check_default);
}
drawable.setBounds(key.x, key.y, key.x + key.width, key.y + key.height);
drawable.draw(canvas);
} else {
Drawable drawable;
if(key.pressed){
drawable = getContext().getResources().getDrawable(R.drawable.common_shape_id_keyboard_key_pressed);
} else {
drawable = getContext().getResources().getDrawable(R.drawable.common_shape_id_keyboard_key_default);
}
drawable.setBounds(key.x, key.y, key.x + key.width, key.y + key.height);
drawable.draw(canvas);
}
//现在这部分逻辑是拉出来的,但完全没有这个必要再判断一次
//参照的其他博客,稍后列出
if (key.label != null) {
//确认和删除按键没设置label
paint.setTextSize(textSize);
paint.setColor(getContext().getResources().getColor(android.R.color.black));
Rect rect = new Rect(key.x, key.y, key.x + key.width, key.y + key.height);
//计算字体的baseline
Paint.FontMetricsInt fontMetrics = paint.getFontMetricsInt();
int baseline = (rect.bottom + rect.top - fontMetrics.bottom - fontMetrics.top) / 2;
// 下面这行是实现水平居中
paint.setTextAlign(Paint.Align.CENTER);
canvas.drawText(key.label.toString(), rect.centerX(), baseline, paint);
} else if(key.codes[0] == Keyboard.KEYCODE_DONE){
paint.setTextSize(textSize);
paint.setColor(getContext().getResources().getColor(android.R.color.white));
Rect rect = new Rect(key.x, key.y, key.x + key.width, key.y + key.height);
Paint.FontMetricsInt fontMetrics = paint.getFontMetricsInt();
int baseline = (rect.bottom + rect.top - fontMetrics.bottom - fontMetrics.top) / 2;
// 下面这行是实现水平居中
paint.setTextAlign(Paint.Align.CENTER);
canvas.drawText("确认", rect.centerX(), baseline, paint);
}
}
}
}
}
3. 简单实现其他效果,封装为工具类,拖动改变光标位置,防止长按、双击弹出系统键盘
(1) 简单封装,任意界面中弹出,把自定义键盘当做View,添加到当前的DecorView中,仅针对一个EditText使用。
//为了能够隐藏Keyboard,所以创建静态变量private static IdKeyboardView idKeyboardView;
//设置目标EditText,但是设置后不会弹出自定义键盘
public static void setCustomIDKeyboard(final Activity activity, final EditText target, final IdKeyboardView.KeyboardActionCallback callback){
target.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
int action = event.getActionMasked();
if(action == MotionEvent.ACTION_UP){
KeyboardUtils.showCustomIDKeyboard(activity, target, callback);
}
return true;
}
});
}
//设置目标EditText,并且设置后弹出自定义键盘
public static void showCustomIDKeyboard(final Activity activity, EditText target, final IdKeyboardView.KeyboardActionCallback callback){
if(System.currentTimeMillis() - lastClickTime <= 500){
return;
}
lastClickTime = System.currentTimeMillis();
//获取当前DecorView
final ViewGroup decorView = (ViewGroup) activity.getWindow().getDecorView();
if(idKeyboardView == null) {
//这里用IdKeyboardView(继承LinearLayout)展示自定义键盘,方便后面加些其他控件
idKeyboardView = new IdKeyboardView(activity);
//设置目标Editext,setTarget会修改目标的Touch事件等,后续有部分代码
idKeyboardView.setTarget(activity, target);
//IdKeyboardView创建接口,用来响应“确认”按钮
//如,在KeyboardView.OnKeyboardActionListener的onKey中调用
idKeyboardView.setActionCallback(callback);
decorView.addView(idKeyboardView);
//addView之后设置LayoutParams才会生效
FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.WRAP_CONTENT);
lp.gravity = Gravity.BOTTOM;
idKeyboardView.setLayoutParams(lp);
}
}
//隐藏弹出的自定义键盘
public static void hideCustomIDKeyboard(Activity activity){
if(idKeyboardView != null){
final ViewGroup decorView = (ViewGroup) activity.getWindow().getDecorView();
decorView.removeView(idKeyboardView);
idKeyboardView = null;
}
}
(2) 为了提供更好的使用效果,对目标EditText设置setOnTouchListener,就防止了长按、双击等弹出系统键盘,但是如果需要这些功能就要自己实现。这里仅支持一个目标EditText
public void setTarget(final Context context, final EditText editText){
//这里就有了局限性,仅支持一个目标EditText
if(editText == this.editText){
return;
}
this.editText = editText;
//获得EditText的画笔
Paint paint = editText.getPaint();
//计算文字宽度,取平均值作为每个字符的宽度,注意不是准确的值
textWidth = (int) (paint.measureText("123456789X") / 10);
this.editText.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
int action = event.getActionMasked();
if(action == MotionEvent.ACTION_UP){
//自己封装的工具类,展示键盘
KeyboardUtils.showCustomIDKeyboard((Activity) context, editText, callback);
} else if(action == MotionEvent.ACTION_MOVE){
//改变光标位置
int index = (int) ((event.getX() - editText.getPaddingLeft()) / textWidth);
if(index <= 0){
editText.setSelection(0);
} else if(index >= editText.getText().length()){
editText.setSelection(editText.getText().length());
} else {
editText.setSelection(index);
}
}
return true;
}
});
}
(3)改变光标样式,设置EditText的textCursorDrawable=“@drawable/xxx”
参考了 https://blog.csdn.net/qq_29983773/article/details/79501658