前言
不啰嗦
正文
對于自定義鍵盤扎拣,官方提供了兩個玩意(再簡單的鍵盤也都要用到):Keyboard和KeyboardView赴肚,當然,為了方便我們會定義一個util:KeyboardUtil
Keyboard
Loads an XML description of a keyboard and stores the attributes of the keys. A keyboard consists of rows of keys.
加載鍵盤的XML描述并存儲鍵的屬性二蓝。鍵盤由一行鍵組成誉券。(還有翻譯,我真貼心)
keyboard是一個xml文件刊愚,下面有個例子:
<?xml version="1.0" encoding="utf-8"?>
<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
android:keyWidth="33%p"
android:horizontalGap="1dp"
android:verticalGap="1dp"
android:keyHeight="54dp">
<Row>
<Key android:codes="49" android:keyLabel="1" android:keyEdgeFlags="left"/>
<Key android:codes="50" android:keyLabel="2" />
<Key android:codes="51" android:keyLabel="3" />
</Row>
<Row>
<Key android:codes="52" android:keyLabel="4" android:keyEdgeFlags="left" />
<Key android:codes="53" android:keyLabel="5" />
<Key android:codes="54" android:keyLabel="6" />
</Row>
<Row>
<Key android:codes="55" android:keyLabel="7" android:keyEdgeFlags="left"/>
<Key android:codes="56" android:keyLabel="8" />
<Key android:codes="57" android:keyLabel="9" />
</Row>
<Row>
<Key android:codes="46" android:keyLabel="." android:keyEdgeFlags="left" />
<Key android:codes="48" android:keyLabel="0" />
<Key android:codes="-5" android:isRepeatable="true"
android:keyIcon="@drawable/delete_tzjesr"/>
</Row>
</Keyboard>
具體描述如下:
屬性 | 描述 |
---|---|
codes | 按鍵對應(yīng)的輸出值踊跟,可以為unicode值或則逗號(,)分割的多個值,也可以為一個字 符串鸥诽。在字符串中通過“\”來轉(zhuǎn)義特殊字符商玫,例如 ‘\n’ 或則 ‘\uxxxx’ 。Codes通常用來定義該鍵的鍵碼牡借,例如上圖中的數(shù)字按鍵1對應(yīng)的為49拳昌;如果提供的是逗號分割的多個值則和普通手機輸入鍵盤一樣在多個值之間切換 |
keyLabel | 按鍵顯示的文本內(nèi)容 |
keyIcon | 按鍵顯示的圖標內(nèi)容,如果指定了該值則在顯示的時候顯示為圖片不顯示文本 |
keyWidth | 按鍵的寬度钠龙,可以為精確值或則相對值炬藤,對于精確值支持多種單位,例如:像素碴里,英寸 等沈矿;相對值為相對于基礎(chǔ)取值的百分比,為以% 或則%p 結(jié)尾并闲,其中%p表示相對于父容器 |
keyHeight | 按鍵的高度细睡,取值同keyWidth |
horizontalGap | 按鍵前的間隙(水平方向),取值取值同keyWidth |
isSticky | 按鍵是否為sticky的帝火。例如Shift大小寫切換按鍵溜徙,具有兩種狀態(tài),按下狀態(tài)和正常狀態(tài)犀填,取值為true或則false |
isModifier | 按鍵是否為功能鍵( modifier key ) 蠢壹,例如 Alt 或則 Shift 。取值為true或則false |
keyOutputText | 按鍵輸出的文本內(nèi)容九巡,取值為字符串 |
isRepeatable | 按鍵是否是可重復的图贸,如果長按該鍵可以觸發(fā)重復按鍵事件則為true,否則為false |
keyEdgeFlags | 按鍵的對齊指令冕广,取值為left或則right |
實例為三列四行標準格式疏日,接下來聊一聊標準:最標準的莫過于每個鍵同樣的高度同樣的寬度,當然這是很理想的一種形式撒汉。我們一般會得到UI強制規(guī)定的個性化鍵盤:
不同顏色的自定義鍵下面說別著急沟优,對于上面這樣的不規(guī)則鍵盤,因為只能一行一行放置鍵睬辐,
所以首先keyWidth我們設(shè)置為25%p我們一行最多要放四個鍵挠阁,一三行我們放四個鍵二四行放三個鍵,第一三行最后一個鍵高度為其他鍵高度的兩倍需要在<key />里面單獨寫keyHeight溯饵,同理如果鍵寬不一樣的話單獨設(shè)置即可侵俗。
KeyboardView
A view that renders a virtual Keyboard. It handles rendering of keys and detecting key presses and touch movements.
呈現(xiàn)虛擬鍵盤的視圖。它處理按鍵的渲染和檢測按鍵和觸摸動作丰刊。
這個東西就是寫在布局里的view了
示例如下:
<com.ql.jcjr.view.MyKeyboredView
android:id="@+id/keyboard_view"
android:layout_width="match_parent"
android:layout_height="216dp"
android:background="@color/divider_color"
android:focusable="true"
android:shadowRadius="0.0"
android:shadowColor="@color/c_fffff"
android:focusableInTouchMode="true"
android:keyBackground="@drawable/keyboard_key"
android:keyTextColor="@color/colorPrimaryDark"
/>
這是我重寫掉的隘谣,后面會講。
具體屬性如下:
屬性 | 描述 |
---|---|
android:keyBackground | 鍵的背景圖 |
android:keyPreviewLayout | 擊鍵盤上的某個鍵時啄巧,短暫彈出的提示布局文件 |
android:keyPreviewOffset | 擊鍵盤上的某個鍵時洪橘,短暫彈出布局的垂直偏移量 |
android:keyTextColor | 按鍵中的keyLabel的顏色 |
android:keyTextSize | 按鍵中的keyLabel的大小 |
android:labelTextSize | 如果有圖片時,按鍵中的keyLabel的大小 |
android:popupLayout | 彈出鍵盤的布局文件 |
android:verticalCorrection | 補償觸摸Y坐標的偏移量棵帽,用于偏移校正 |
android:shadowRadius="0.0" android:shadowColor="背景色"
這兩個屬性可以解決鍵盤上顯示的文字糊的問題熄求。
KeyboardUtil
public class KeyboardUtil {
private View ll_jianpan;
private KeyboardView mKeyboardView;
private Keyboard mNumberKeyboard; // 數(shù)字鍵盤
private Keyboard mLetterKeyboard; // 字母鍵盤
private Context mContext;
private boolean isNumber = false; // 是否數(shù)字鍵盤
private boolean isUpper = false; // 是否大寫
private EditText mEditText;
/**
*
* @param context
* @param view
* @param editText
* @param type 0,空白格 1,小數(shù)點 2逗概,身份證X
*/
public KeyboardUtil(Context context, View view, EditText editText,int type) {
mContext=context;
mEditText = editText;
mLetterKeyboard = new Keyboard(context, R.xml.keyboard_qwerty);
mKeyboardView = (KeyboardView) view.findViewById(R.id.keyboard_view);
ll_jianpan= view.findViewById(R.id.ll_jianpan);
ImageView iv_close= (ImageView) ll_jianpan.findViewById(R.id.iv_close);
iv_close.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
hideKeyboard();
}
});
if (type==0){
mNumberKeyboard = new Keyboard(context, R.xml.keyboard_numbers_blank);
}else if (type==1){
mNumberKeyboard = new Keyboard(context, R.xml.keyboard_numbers);
}else if (type==2){
mNumberKeyboard = new Keyboard(context, R.xml.keyboard_numbers_realname);
}else if (type==66299){
mNumberKeyboard = new Keyboard(context, R.xml.keyboard_numbers_blank);
iv_close.setVisibility(View.GONE);
}
mKeyboardView.setKeyboard(mNumberKeyboard);
mKeyboardView.setEnabled(true);
mKeyboardView.setPreviewEnabled(false);
mKeyboardView.setOnKeyboardActionListener(listener);
}
public KeyboardUtil(Context context, Activity activity, EditText editText, int type) {
mContext=context;
mEditText = editText;
if (type==0){
mNumberKeyboard = new Keyboard(context, R.xml.keyboard_numbers_blank);
}else if (type==1){
mNumberKeyboard = new Keyboard(context, R.xml.keyboard_numbers);
}else if (type==2){
mNumberKeyboard = new Keyboard(context, R.xml.keyboard_numbers_realname);
}
mLetterKeyboard = new Keyboard(context, R.xml.keyboard_qwerty);
mKeyboardView = (KeyboardView) activity.findViewById(R.id.keyboard_view);
ll_jianpan= activity.findViewById(R.id.ll_jianpan);
mKeyboardView.setKeyboard(mNumberKeyboard);
mKeyboardView.setEnabled(true);
mKeyboardView.setPreviewEnabled(false);
mKeyboardView.setOnKeyboardActionListener(listener);
ImageView iv_close= (ImageView) ll_jianpan.findViewById(R.id.iv_close);
iv_close.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
hideKeyboard();
}
});
}
private KeyboardView.OnKeyboardActionListener listener = new KeyboardView.OnKeyboardActionListener() {
@Override
public void onPress(int primaryCode) {
}
@Override
public void onRelease(int primaryCode) {
}
@Override
public void onKey(int primaryCode, int[] keyCodes) {
Editable editable = mEditText.getText();
int start = mEditText.getSelectionStart();
if (primaryCode == Keyboard.KEYCODE_CANCEL||primaryCode == 66299) { // cancel
hideKeyboard();
} else if (primaryCode == Keyboard.KEYCODE_DELETE) { // 回退
if (editable != null && editable.length() > 0) {
if (start > 0) {
editable.delete(start - 1, start);
}
}
} else if (primaryCode == Keyboard.KEYCODE_SHIFT) { // 大小寫切換
changeKeyboart();
mKeyboardView.setKeyboard(mLetterKeyboard);
} else if (primaryCode == Keyboard.KEYCODE_MODE_CHANGE) { // 數(shù)字與字母鍵盤互換
if (isNumber) {
isNumber = false;
mKeyboardView.setKeyboard(mLetterKeyboard);
} else {
isNumber = true;
mKeyboardView.setKeyboard(mNumberKeyboard);
}
} else if (primaryCode == 57419) { // 左移
if (start > 0) {
mEditText.setSelection(start - 1);
}
} else if (primaryCode == 57419) { // 右移
if (start > mEditText.length()) {
mEditText.setSelection(start + 1);
}
} else { // 輸入鍵盤值
editable.insert(start, Character.toString((char) primaryCode));
}
}
@Override
public void onText(CharSequence text) {
}
@Override
public void swipeLeft() {
}
@Override
public void swipeRight() {
}
@Override
public void swipeDown() {
}
@Override
public void swipeUp() {
}
};
private void changeKeyboart() {
List<Keyboard.Key> keyList = mLetterKeyboard.getKeys();
if (isUpper) { // 大寫切換小寫
isUpper = false;
for (Keyboard.Key key : keyList) {
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() {
int visibility = ll_jianpan.getVisibility();
if (visibility == View.VISIBLE) {
ll_jianpan.startAnimation(AnimationUtils.loadAnimation(mContext,R.anim.anim_bottom_out));
ll_jianpan.setVisibility(View.GONE);
}
}
public void showKeyboard() {
int visibility = ll_jianpan.getVisibility();
if (visibility == View.GONE || visibility == View.INVISIBLE) {
ll_jianpan.startAnimation(AnimationUtils.loadAnimation(mContext,R.anim.anim_bottom_in));
ll_jianpan.setVisibility(View.VISIBLE);
}
}
}
ll_jianpan其實就是一個包含keyboradview的線性布局
在這里比較重要的就是實現(xiàn)鍵盤的監(jiān)聽OnKeyboardActionListener
下面有一張對應(yīng)表code對應(yīng)ASCII值 然后輸出的就是控制字符 :
當然我們肯定需要自定義一些功能弟晚,如關(guān)閉鍵盤切換數(shù)字字母鍵盤左移右移等,這些都放在了OnKeyboardActionListener的onKey中逾苫。其實就是根據(jù)不同的code處理不同的事件卿城,自定義code盡量個性點防止跟已占用code發(fā)生沖突。
既然是自定義鍵盤铅搓,我們當然要阻攔系統(tǒng)鍵盤瑟押,方法如下:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
editText.getCancelEditText().setShowSoftInputOnFocus(false);
}else {
editText.getCancelEditText().setInputType(InputType.TYPE_NULL);
}
PS:setInputType(InputType.TYPE_NULL)會讓輸入框失去光標,但是沒辦法21以下的沒有setShowSoftInputOnFocus(false)這方法星掰。
至于打開鍵盤多望,可以在觸摸輸入框的時候觸發(fā)也可直接觸發(fā):
editText.getCancelEditText().setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
new KeyboardUtil(mContext, Activity.this, editText,0).showKeyboard();
return false;
}
});
最后嫩舟,重寫的Keyboradview:
public class MyKeyboredView extends KeyboardView {
public MyKeyboredView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyKeyboredView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public MyKeyboredView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
List<Keyboard.Key> keys = getKeyboard().getKeys();
for (Keyboard.Key key : keys) {
if (key.codes[0] == 66299) {
Log.e("KEY", "Drawing key with code " + key.codes[0]);
Drawable dr = (Drawable) JcbApplication.getInstance().getResources().getDrawable(R.drawable.jianpan_sure);
dr.setBounds(key.x, key.y, key.x + key.width, key.y + key.height);
dr.draw(canvas);
} else {
}
}
}
}
重寫onDraw()根據(jù)code把點九圖充滿指定的鍵即可
后記
看到這你可能在找demo源碼,不好意思沒有的怀偷,如果你的時間確實很緊請移步家厌,如果你還有點時間,一天自己學完寫完沒什么問題的椎工,歡迎提出問題饭于,我們一起學習。