簡介
在一些帶有支付功能的 App 中,輸入的密碼一般只能是純數字,雖然我們可以指定 EditText 輸入框只能輸入數字,但是爲了提供用戶的使用體驗,我們往往更傾向於使用自定義的純數字鍵盤。
本文效果:
自定義 KeyboardView
實現步驟:
- 集成系統的 KeyBoardView 類,在初始化時初始化鍵盤佈局,設置 KeyBoard 對象。
- 實現 OnKeyboardActionListener 接口,處理按鍵交互事件。
- 根據需求繪製按鍵背景和按鍵圖標。
- 設置監聽器,將輸入的內容回調給調用方。
鍵盤佈局
在 res/xml/ 目錄下創建 xml 文件:key_password_number.xml
<?xml version="1.0" encoding="utf-8"?>
<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
android:horizontalGap="1dp"
android:keyHeight="9%p"
android:keyWidth="33.3333%p"
android:verticalGap="1dp">
<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="-10"
android:keyLabel="" />
<Key
android:codes="48"
android:keyLabel="0" />
<Key
android:codes="-5"
android:keyLabel="" />
</Row>
</Keyboard>
繼承 KeyBoardView
public class PwdKeyboardView extends KeyboardView implements KeyboardView.OnKeyboardActionListener {
private static final String TAG = "PwdKeyboardView";
private static final int KEY_EMPTY = -10;
private int delKeyBackgroundColor = 0xffcccccc;
private Rect keyIconRect;
public PwdKeyboardView(Context context, AttributeSet attrs) {
super(context, attrs);
Log.d(TAG, "PwdKeyboardView: two params");
init(context);
}
public PwdKeyboardView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
Log.d(TAG, "PwdKeyboardView: three params");
init(context);
}
private void init(Context context) {
Keyboard keyboard = new Keyboard(context, R.xml.key_password_number); // 初始化 keyboard
setKeyboard(keyboard);
setEnabled(true);
setFocusable(true);
setPreviewEnabled(false); // 設置點擊按鍵不顯示預覽氣泡
setOnKeyboardActionListener(this);
}
/**
* 重新繪製刪除按鍵和空白鍵
*
* @param canvas
*/
@Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
List<Keyboard.Key> keys = getKeyboard().getKeys();
for (Keyboard.Key key : keys) {
if (key.codes[0] == KEY_EMPTY) {
// 繪製空白鍵背景
drawKeyBackground(key, canvas, delKeyBackgroundColor);
}
if (key.codes[0] == Keyboard.KEYCODE_DELETE) {
// 刪除刪除按鍵背景
drawKeyBackground(key, canvas, delKeyBackgroundColor);
// 繪製刪除按鍵圖標
drawKeyIcon(key, canvas, getResources().getDrawable(R.drawable.ic_delete));
}
}
}
/**
* 繪製按鍵的背景
*
* @param key
* @param canvas
* @param color
*/
private void drawKeyBackground(Keyboard.Key key, Canvas canvas, int color) {
ColorDrawable drawable = new ColorDrawable(color);
drawable.setBounds(key.x, key.y, key.x + key.width, key.y + key.height);
drawable.draw(canvas);
}
/**
* 繪製按鍵的 icon
*
* @param key
* @param canvas
* @param iconDrawable
*/
private void drawKeyIcon(Keyboard.Key key, Canvas canvas, Drawable iconDrawable) {
if (iconDrawable == null) {
return;
}
// 計算按鍵icon 的rect 範圍
if (keyIconRect == null || keyIconRect.isEmpty()) {
// 得到 keyicon 的顯示大小,因爲圖片放在不同的drawable-dpi目錄下,顯示大小也不一樣
int intrinsicWidth = iconDrawable.getIntrinsicWidth();
int intrinsicHeight = iconDrawable.getIntrinsicHeight();
int drawWidth = intrinsicWidth;
int drawHeight = intrinsicHeight;
// 限制圖片的大小,防止圖片按鍵範圍
if (drawWidth > key.width) {
drawWidth = key.width;
// 此時高就按照比例縮放
drawHeight = (int) (drawWidth * 1.0f / intrinsicWidth * intrinsicHeight);
} else if (drawHeight > key.height) {
drawHeight = key.height;
drawWidth = (int) (drawHeight * 1.0f / intrinsicHeight * intrinsicWidth);
}
// 獲取圖片的 x,y 座標,圖片在按鍵的正中間
int left = key.x + key.width / 2 - drawWidth / 2;
int top = key.y + key.height / 2 - drawHeight / 2;
keyIconRect = new Rect(left, top, left + drawWidth, top + drawHeight);
}
if (keyIconRect != null && !keyIconRect.isEmpty()) {
iconDrawable.setBounds(keyIconRect);
iconDrawable.draw(canvas);
}
}
@Override
public void onPress(int primaryCode) {
}
@Override
public void onRelease(int primaryCode) {
}
/**
* 處理按鍵的點擊事件
*/
@Override
public void onKey(int primaryCode, int[] keyCodes) {
Log.d(TAG, "onKey: primaryCode = " + primaryCode + ", keyCodes = " + Arrays.toString(keyCodes));
if (primaryCode == KEY_EMPTY) {
return;
}
if (listener != null) {
if (primaryCode == Keyboard.KEYCODE_DELETE) {
listener.onDelete();
} else {
listener.onInput(String.valueOf((char) primaryCode));
}
}
}
@Override
public void onText(CharSequence charSequence) {
}
@Override
public void swipeLeft() {
}
@Override
public void swipeRight() {
}
@Override
public void swipeDown() {
}
@Override
public void swipeUp() {
}
public interface OnKeyListener {
// 輸入回調
void onInput(String text);
// 刪除回調
void onDelete();
}
private OnKeyListener listener;
public void setOnKeyListener(OnKeyListener listener) {
this.listener = listener;
}
}
使用 PwdKeyboardView
<com.xing.pwdkeyboardview.PwdKeyboardView
android:id="@+id/key_board"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#919191"
android:keepScreenOn="true"
android:keyBackground="@drawable/selector_key_board" <! --設置按鍵的背景選擇器 -->
android:keyTextColor="@android:color/black"
android:keyTextSize="26sp"
android:shadowRadius="0" /> <!-- shadowRadius = 0 ,防止按鍵數字顯示模糊 --->
顯示結果爲: