其实对于简单的键盘,比如数字键盘这种,使用自定义view即可,这里为了学习android的键盘知识,继承android系统键盘。
先看效果图:
一、在res/xml目录下新建一个键盘布局文件
<?xml version="1.0" encoding="utf-8"?>
<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
android:keyWidth="22.5%p"
android:keyHeight="8%p"
android:horizontalGap="1%p"
android:verticalGap="0.5%p"
>
<Row>
<Key android:codes="1" android:keyLabel="1" android:isRepeatable="true"/>
<Key android:codes="2" android:keyLabel="2" android:isRepeatable="true"/>
<Key android:codes="3" android:keyLabel="3" android:isRepeatable="true"/>
<Key android:codes="-5" android:keyIcon="@drawable/ic_delete_key" android:isRepeatable="true"/>
</Row>
<Row>
<Key android:codes="4" android:keyLabel="4" android:isRepeatable="true"/>
<Key android:codes="5" android:keyLabel="5" android:isRepeatable="true"/>
<Key android:codes="6" android:keyLabel="6" android:isRepeatable="true"/>
<Key android:codes="-2" android:keyHeight="25%p"/>
</Row>
<Row>
<Key android:codes="7" android:keyLabel="7" android:isRepeatable="true"/>
<Key android:codes="8" android:keyLabel="8" android:isRepeatable="true"/>
<Key android:codes="9" android:keyLabel="9" android:isRepeatable="true"/>
</Row>
<Row>
<Key android:codes="0" android:keyLabel="0" android:keyWidth="46%p" android:isRepeatable="true"/>
<Key android:codes="-3" android:keyLabel="."/>
</Row>
</Keyboard>
keyWidth和keyHeight分别代表按键的宽高,可以用百分比或者数值的方式。
horizontalGap和verticalGap分别代表横向和竖向的按键间隔。
codes是按键编号,可自定义。
keyLabel在按键上显示的字体。
isRepeatable长按重复点击。
keyIcon按钮图标,但是好像不能自动显示出来,需要在自定义的KeyboardView中去绘制。
二、继承KeyboardView
public class PayKeyboardView extends KeyboardView {
private Paint payPaint;
private boolean enablePay; // 是否允许付款
public PayKeyboardView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public PayKeyboardView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
payPaint = new Paint();
payPaint.setTextAlign(Paint.Align.CENTER);
payPaint.setTextSize(ScreenUtils.sp2px(getContext(), 24));
payPaint.setColor(Color.WHITE);
payPaint.setAntiAlias(true);
}
@Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
Keyboard keyboard = getKeyboard();
if (keyboard == null) return;
List<Keyboard.Key> keys = keyboard.getKeys();
if (keys != null && keys.size() > 0) {
for (Keyboard.Key key : keys) {
if (key.codes[0] == -5) {
// 删除
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);
}
} else if (key.codes[0] == -2) {
// 付款
Drawable dr;
if (enablePay) {
dr = getContext().getResources().getDrawable(R.drawable.bg_pay);
} else {
dr = getContext().getResources().getDrawable(R.drawable.bg_pay_n);
}
dr.setBounds(key.x, key.y, key.x + key.width, key.y + key.height);
dr.draw(canvas); // 绘制付款背景
// 竖向绘制付款文字
String text = "付款";
Rect rect = new Rect(key.x, key.y, key.x + key.width, key.y + key.height);
Paint.FontMetricsInt fontMetrics = payPaint.getFontMetricsInt();
int baseline = (rect.bottom + rect.top) / 2 - fontMetrics.bottom;
payPaint.setTextAlign(Paint.Align.CENTER);
for (int i=0;i<text.length();i++) {
canvas.drawText(String.valueOf(text.charAt(i)), rect.centerX(), baseline, payPaint);
baseline += (fontMetrics.bottom - fontMetrics.top);
}
}
}
}
}
/**
* 允许付款
* @param pay
*/
public void enablePay(boolean pay) {
if (enablePay == pay) {
return;
}
enablePay = pay;
invalidate();
}
/**
* 是否允许付款
*/
public boolean getEnablePay() {
return enablePay;
}
}
该类主要实现键盘按钮的绘制,包括一些个性化的实现。
三、在xml布局中使用
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white"
android:gravity="center_horizontal"
android:orientation="vertical">
<com.showfitness.commonlibrary.widget.TitleBarView
android:id="@+id/title_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:title_name="向商家付款" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dp_32"
android:src="@drawable/ic_shop"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="@dimen/dp_10"
android:paddingBottom="@dimen/dp_10"
android:layout_marginLeft="@dimen/dp_30"
android:layout_marginRight="@dimen/dp_30"
android:layout_marginTop="@dimen/dp_42">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="¥"
android:textColor="#000000"
android:textSize="24sp"/>
<EditText
android:id="@+id/et_money"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/dp_20"
android:background="@null"
android:textSize="24sp"
android:focusable="true"
android:focusableInTouchMode="true"
android:textStyle="bold"
android:textColor="@color/common_black" />
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1px"
android:layout_marginLeft="@dimen/dp_20"
android:layout_marginRight="@dimen/dp_20"
android:background="#EEEEEE"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#EBEBEB"
android:layout_alignParentBottom="true"
android:gravity="center_horizontal">
<com.showfitness.commonlibrary.widget.keyboard.PayKeyboardView
android:id="@+id/keyboard"
android:layout_width="match_parent"
android:layout_height="@dimen/dp_300"
android:layout_marginTop="@dimen/dp_5"
android:layout_marginBottom="@dimen/dp_5"
android:background="#EBEBEB"
android:keyBackground="@drawable/bg_keyboard_selector"
android:keyTextColor="@color/keyboard_text"
android:keyTextSize="28sp"
android:shadowRadius="0.0" />
</LinearLayout>
</RelativeLayout>
由于我们继承的系统的KeyboardView,在布局中可以使用一些系统定义的属性。
keyBackground -> 按钮的背景,可以设置按压状态
keyTextColor -> 键盘文字颜色
keyTextSize -> 键盘文字大小
shadowRadius=“0.0” -> 设置这行,不然文字会模糊
等等
四、自定义键盘辅助类
public class CustomerKeyboardUtil {
private Context mContext;
private PayKeyboardView mKeyboardView;
private EditText mEditText;
private Listener listener;
@SuppressLint("ClickableViewAccessibility")
public CustomerKeyboardUtil(Context context, PayKeyboardView keyboardView, EditText editText) {
this.mContext = context;
this.mKeyboardView = keyboardView;
this.mEditText = editText;
initKeyboard();
mEditText.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
// 文本框点击事件,隐藏系统键盘,显示自定义键盘
if (event.getAction() == MotionEvent.ACTION_UP) {
hideSystemKeyboard((EditText) v);
mKeyboardView.setVisibility(View.VISIBLE);
}
return false;
}
});
mEditText.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
// 当失去焦点时,隐藏键盘,但是没啥用
if (v instanceof EditText) {
if (!hasFocus) {
mKeyboardView.setVisibility(View.GONE);
} else {
hideSystemKeyboard((EditText) v);
mKeyboardView.setVisibility(View.VISIBLE);
}
}
}
});
// 自动获取焦点
mEditText.requestFocus();
}
/**
* 隐藏系统键盘
* @param v
*/
private void hideSystemKeyboard(EditText v) {
this.mEditText = v;
InputMethodManager imm = (InputMethodManager) mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
if(imm == null){
return;
}
boolean isOpen = imm.isActive();
if (isOpen) {
imm.hideSoftInputFromWindow(v.getWindowToken(),0);
}
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){
v.setShowSoftInputOnFocus(false);
}else{
v.setInputType(0);
}
}
/**
* 初始化自定义键盘
*/
private void initKeyboard() {
Keyboard keyboard = new Keyboard(mContext, R.xml.customer_keyboard); // 绑定自定义键盘视图
mKeyboardView.setKeyboard(keyboard);
mKeyboardView.setEnabled(true);
mKeyboardView.setOnKeyboardActionListener(actionListener);
}
private KeyboardView.OnKeyboardActionListener actionListener = new KeyboardView.OnKeyboardActionListener() {
@Override
public void onPress(int primaryCode) {
// 禁止预览
mKeyboardView.setPreviewEnabled(false);
}
@Override
public void onRelease(int primaryCode) {
}
@Override
public void onKey(int primaryCode, int[] keyCodes) {
Editable editable = mEditText.getText();
int start = mEditText.getSelectionStart();
int end = mEditText.getSelectionEnd();
if(primaryCode == Keyboard.KEYCODE_DELETE){
// 删除
if(editable != null && editable.length() > 0){
if(start == end){
editable.delete(start -1, start);
}else{
editable.delete(start,end);
}
}
} else if(primaryCode == -3){
// 小数点
if (TextUtils.isEmpty(editable) || editable.toString().contains(".")) {
// 小数点不能是第一个,只能有一个小数点
return;
}
mEditText.append(".");
} else if(primaryCode == -2){
// 付款
if (mKeyboardView.getEnablePay()) {
if (listener != null) {
listener.onPay(getText());
}
}
} else if(primaryCode == 0){
// 按键0
if (!TextUtils.isEmpty(editable) && editable.toString().startsWith("0")) {
// 0不能前面连续两个
return;
}
mEditText.append(String.valueOf(primaryCode));
} else {
// 其他数字
if (!TextUtils.isEmpty(editable) && editable.toString().startsWith("0")) {
// 第一个为零,后一位只能是小数点
return;
}
mEditText.append(String.valueOf(primaryCode));
}
mKeyboardView.enablePay(!TextUtils.isEmpty(mEditText.getText()));
if (listener != null) {
listener.onChange(getText());
}
}
@Override
public void onText(CharSequence text) {
}
@Override
public void swipeLeft() {
}
@Override
public void swipeRight() {
}
@Override
public void swipeDown() {
}
@Override
public void swipeUp() {
}
};
/**
* 格式化结果
* @return
*/
private String getText() {
String text = mEditText.getText().toString();
if (TextUtils.isEmpty(mEditText.getText())) {
return text;
}
if (text.endsWith(".")) {
// 去掉最后一位是小数点
text.replace(".", "");
}
return text;
}
public Listener getListener() {
return listener;
}
/**
* 是否点击键盘
* @return
*/
public boolean isClickKeyboardView(int x, int y) {
Rect rect = new Rect();
mKeyboardView.getGlobalVisibleRect(rect);
return rect.contains(x, y);
}
/**
* 隐藏软键盘
*/
public void hideKeyboard() {
mKeyboardView.setVisibility(View.GONE);
}
public void setListener(Listener listener) {
this.listener = listener;
}
/**
* 支付监听
*/
public interface Listener {
void onChange(CharSequence text);
// 支付
void onPay(CharSequence text);
}
}
将键盘和文本绑定在一起,监听键盘事件。
五、在activity中使用
public class PayStoreActivity extends BaseActivity {
private CustomerKeyboardUtil customerKeyboardUtil;
@BindView(R2.id.et_money)
EditText etMoney;
@BindView(R2.id.keyboard)
PayKeyboardView keyboardView;
@Override
protected int getLayoutId() {
return R.layout.activity_pay_store;
}
@Override
public void onBindView(Bundle savedInstanceState) {
// 绑定键盘辅助类
customerKeyboardUtil = new CustomerKeyboardUtil(mContext, keyboardView, etMoney);
customerKeyboardUtil.setListener(new CustomerKeyboardUtil.Listener() {
@Override
public void onChange(CharSequence text) {
}
@Override
public void onPay(CharSequence text) {
// 去付款
}
});
}
/**
* 当点击空白区域时,收起键盘
* @param me
* @return
*/
@Override
public boolean dispatchTouchEvent(MotionEvent me) {
if (me.getAction() == MotionEvent.ACTION_DOWN) { //把操作放在用户点击的时候
View v = getCurrentFocus(); //得到当前页面的焦点,ps:有输入框的页面焦点一般会被输入框占据
if (isShouldHideKeyboard(v, me)) { //判断用户点击的是否是输入框以外的区域
customerKeyboardUtil.hideKeyboard(); //收起键盘
}
}
return super.dispatchTouchEvent(me);
}
/**
* 根据EditText所在坐标和用户点击的坐标相对比,来判断是否隐藏键盘,因为当用户点击EditText时则不能隐藏
*
* @param v
* @param event
* @return
*/
private boolean isShouldHideKeyboard(View v, MotionEvent event) {
if (v != null && (v instanceof EditText)) { //判断得到的焦点控件是否包含EditText
int[] location = {0, 0};
v.getLocationInWindow(location);
int left = location[0], //得到输入框在屏幕中上下左右的位置
top = location[1],
bottom = top + v.getHeight(),
right = left + v.getWidth();
if ((event.getX() > left && event.getX() < right
&& event.getY() > top && event.getY() < bottom) ||
(customerKeyboardUtil.isClickKeyboardView((int)event.getX(), (int)event.getY()))) {
// 点击位置如果是EditText的区域或者键盘区域,忽略它,不收起键盘。
return false;
} else {
return true;
}
}
// 如果焦点不是EditText则忽略
return false;
}
}
这里处理了点击空白处收起键盘的操作。