Android 自定義輸入軟鍵盤
阿新 • • 發佈:2018-12-11
前言
在日常開發中,有的時候我們需要使用者輸入指定範圍的內容,除了給與充分的文字提示,更加人性化的就是定製一個自定義鍵盤。
Android的自定義鍵盤常用於密碼輸入時的安全鍵盤,比如支付寶支付時。
如上圖,在輸入體溫時,彈出一個自定義的體溫鍵盤,這樣既能人性化服務,也能規避絕大多數非法數值的輸入。
實現
Keyboard
官方上對Keyboard的解釋:
載入鍵盤的XML描述並存儲鍵的屬性。
鍵盤由鍵行組成。
佈局檔案為鍵盤包含類似於以下程式碼段的XML。
屬性 | 型別 | 描述 |
---|---|---|
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按鍵間隙(垂直),同上 |
codes | int | Codes通常用來定義該鍵的鍵碼,按鍵對應的輸出值,可以為unicode值或則逗號(,)分割的多個值,也可以為一個字串。在字串中通過“\”來轉義特殊字元,例如 ‘\n’ 或則 ‘\uxxxx’ 。 |
iconPreview | reference | 彈出回顯的icon |
isModifier | boolean | 是否功能修飾鍵,如:Alt/Shift |
isSticky | boolean | 是否是開關按鍵 |
isRepeatable | boolean | 是否允許重複。true表示長按時重複執行。 |
keyEdgeFlags | enum | Key邊緣位置標記,left/right,鍵盤左(右)邊錨點。 |
keyIcon | reference | 替換label顯示在按鍵上的icon。 |
keyLabel | reference | 顯示在Key上的標籤。 |
keyOutputText | string | Key按下時輸出的字元或字串。 |
popupCharacters | string | 小鍵盤顯示的字元,用於顯示Key候選項。 |
popupKeyboard | reference | 按鍵候選小鍵盤的keyboard佈局。 |
keyboard_temp.xml
<?xml version="1.0" encoding="utf-8"?>
<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
android:keyWidth="20%p"
android:keyHeight="45dp">
<Row>
<Key
android:keyLabel="33."
android:keyOutputText="33." />
<Key
android:keyLabel="34."
android:keyOutputText="34." />
<Key
android:codes="49"
android:keyLabel="1" />
<Key
android:codes="50"
android:keyLabel="2" />
<Key
android:codes="-3"
android:keyIcon="@drawable/base_keyboard_hide" />
</Row>
<Row>
<Key
android:keyLabel="35."
android:keyOutputText="35." />
<Key
android:keyLabel="36."
android:keyOutputText="36." />
<Key
android:codes="51"
android:keyLabel="3" />
<Key
android:codes="52"
android:keyLabel="4" />
<Key
android:codes="-5"
android:isRepeatable="true"
android:keyIcon="@drawable/base_backspace" />
</Row>
<Row>
<Key
android:keyLabel="37."
android:keyOutputText="37." />
<Key
android:keyLabel="38."
android:keyOutputText="38." />
<Key
android:codes="53"
android:keyLabel="5" />
<Key
android:codes="54"
android:keyLabel="6" />
<Key
android:codes="-9"
android:keyLabel="全選" />
</Row>
<Row>
<Key
android:keyLabel="39."
android:keyOutputText="39." />
<Key
android:keyLabel="40."
android:keyOutputText="40." />
<Key
android:codes="55"
android:keyLabel="7" />
<Key
android:codes="56"
android:keyLabel="8" />
<Key
android:codes="-7"
android:keyLabel="上一項" />
</Row>
<Row>
<Key
android:keyLabel="41."
android:keyOutputText="41." />
<Key
android:keyLabel="42."
android:keyOutputText="42." />
<Key
android:codes="57"
android:keyLabel="9" />
<Key
android:codes="48"
android:keyLabel="0" />
<Key
android:codes="-8"
android:keyLabel="下一項" />
</Row>
</Keyboard>
注意
- 系統預設的幾個KeyCode,無需自定義。
public static final int KEYCODE_SHIFT = -1; //shift
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; //alt
- 當key中有keyOutputText屬性時,點選鍵盤會觸發監聽函式的onText(CharSequence text) 方法。
- codes屬性可以省略,預設使用keyLabel字元的Unicode值。功能鍵等其他自定義按鍵的keycode建議設定為第一無二的負數(為了不與預設及其他按鍵衝突,正數多為ASCll碼佔用)。
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 | 按鍵文字陰影顏色,預設有陰影,無需陰影值為0即可 |
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:focusable="true"
android:focusableInTouchMode="true">
<EditText
android:id="@+id/et"
android:layout_width="match_parent"
android:layout_height="50dp" />
<android.inputmethodservice.KeyboardView
android:id="@+id/keyboard_temp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:visibility="gone"
android:background="@drawable/keyboard_background"
android:focusable="true"
android:focusableInTouchMode="true"
android:keyBackground="@drawable/key_background"
android:keyTextColor="#000000"
android:paddingTop="2dp"
android:paddingBottom="2dp"
android:shadowRadius="0.0" />
</RelativeLayout>
配置及監聽
public class KeyboardTEMPHelper {
public static final int LAST = -7;
public static final int NEXT = -8;
public static final int ALL = -9;
private Context context;
private KeyboardView keyboardView;
private EditText editText; //顯示該鍵盤的EditText
private Keyboard k1;// 自定義鍵盤
private KeyboardCallBack callBack;//按鍵回撥監聽
public KeyboardTEMPHelper(Context context, KeyboardView keyboardView) {
this(context, keyboardView, null);
}
public KeyboardTEMPHelper(Context context, KeyboardView keyboardView, KeyboardCallBack callBack) {
this.context = context;
k1 = new Keyboard(context, R.xml.keyboard_temp);//據Keyboard的xml佈局繫結
this.keyboardView = keyboardView;
this.keyboardView.setOnKeyboardActionListener(listener);//設定鍵盤監聽
this.keyboardView.setKeyboard(k1);//設定預設鍵盤
this.keyboardView.setEnabled(true);
this.keyboardView.setPreviewEnabled(false);
this.callBack = callBack;
}
private KeyboardView.OnKeyboardActionListener listener = new KeyboardView.OnKeyboardActionListener() {
@Override
public void swipeUp() {
}
@Override
public void swipeRight() {
}
@Override
public void swipeLeft() {
}
@Override
public void swipeDown() {
}
@Override
public void onText(CharSequence text) {
//當key中有keyOutputText屬性時,點選鍵盤會觸發該方法,回撥keyOutputText的值
Editable editable = editText.getText();
int end = editText.getSelectionEnd();
editable.delete(0, end);
editable.insert(0, text);
}
@Override
public void onRelease(int primaryCode) {
}
@Override
public void onPress(int primaryCode) {
}
@Override
public void onKey(int primaryCode, int[] keyCodes) {
//設定了codes屬性後,點選鍵盤會觸發該方法,回撥codes的值
//codes值與ASCLL碼對應
Editable editable = editText.getText();
int start = editText.getSelectionStart();
int end = editText.getSelectionEnd();
switch (primaryCode) {
case Keyboard.KEYCODE_DELETE:
if (editable != null && editable.length() > 0) {
if (start == end) {
editable.delete(start - 1, start);
} else {
editable.delete(start, end);
}
}
break;
case Keyboard.KEYCODE_CANCEL:
keyboardView.setVisibility(View.GONE);
break;
case ALL:
editText.selectAll();
break;
case LAST:
case NEXT:
break;
default:
if (start != end) {
editable.delete(start, end);
}
editable.insert(start, Character.toString((char) primaryCode));
break;
}
if (callBack != null) {
callBack.keyCall(primaryCode);
}
}
};
//在顯示鍵盤前應呼叫此方法,指定EditText與KeyboardView繫結
public void setEditText(EditText editText) {
this.editText = editText;
//關閉進入該介面獲取焦點後彈出的系統鍵盤
InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm != null) {
imm.hideSoftInputFromWindow(editText.getWindowToken(), 0);
}
//隱藏該EditText獲取焦點而要彈出的系統鍵盤
KeyboardUtil.hideSoftInput(editText);
}
//Activity中獲取焦點時呼叫,顯示出鍵盤
public void show() {
int visibility = keyboardView.getVisibility();
if (visibility == View.GONE || visibility == View.INVISIBLE) {
keyboardView.setVisibility(View.VISIBLE);
}
}
//隱藏鍵盤
public void hide() {
int visibility = keyboardView.getVisibility();
if (visibility == View.VISIBLE) {
keyboardView.setVisibility(View.GONE);
}
}
public boolean isVisibility() {
if (keyboardView.getVisibility() == View.VISIBLE) {
return true;
} else {
return false;
}
}
public interface KeyboardCallBack {
void keyCall(int code);
}
//設定回撥,用於自定義特殊按鍵在不同介面或EditText的處理
public void setCallBack(KeyboardCallBack callBack) {
this.callBack = callBack;
}
}
使用
MainActivity.java
public class MainActivity extends AppCompatActivity {
private KeyboardTEMPHelper helper;
private EditText editText;
private KeyboardView keyboard;
@SuppressLint("ClickableViewAccessibility")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
keyboard = findViewById(R.id.keyboard_temp);
editText = findViewById(R.id.et);
//初始化KeyboardView
helper = new KeyboardTEMPHelper(MainActivity.this, keyboard);
//設定editText與KeyboardView繫結
helper.setEditText(editText);
helper.setCallBack(new KeyboardTEMPHelper.KeyboardCallBack() {
@Override
public void keyCall(int code) {
//回撥鍵盤監聽,根據回撥的code值進行處理
}
});
editText.setOnTouchListener(new