Android自定义安全键盘
在银行APP里经常要自定义键盘,例如实现下面这样的效果
![](https://images2017.cnblogs.com/blog/827512/201711/827512-20171106160057263-8873308.jpg)
![](https://images2017.cnblogs.com/blog/827512/201711/827512-20171106160057638-961667353.jpg)
首先在xml文件里定义键盘
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | <Keyboard xmlns:android= "http://schemas.android.com/apk/res/android" android:keyWidth= "30%p" android:horizontalGap= "@dimen/keyboard_horizontalGap" android:verticalGap= "@dimen/keyboard_verticalGap" android:keyHeight= "47dp" > <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= "-2" android:keyLabel= "ABC" /> <Key android:codes= "48" android:keyLabel= "0" /> <Key android:codes= "-35" android:isRepeatable= "true" /> </Row> </Keyboard> |
keyWidth:每一个按钮的宽度
keyHeight:每一个按钮高度,可以设置百分比
horizontalGap:水平间隔
verticalGap:竖直间隔
Row:一行
每一个按键都将会有一个 codes 值,代表键盘上的按键
KhKeyboardView
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 | public class KhKeyboardView { private Activity mContext; private View parentView; private KeyboardView mLetterView; //字母键盘view private KeyboardView mNumberView; //数字键盘View private Keyboard mNumberKeyboard; // 数字键盘 private Keyboard mLetterKeyboard; // 字母键盘 private Keyboard mSymbolKeyboard; // 符号键盘 private boolean isNumber = true ; // 是否数字键盘 public static boolean isUpper = false ; // 是否大写 private boolean isSymbol = false ; // 是否是符号 private EditText mEditText; private View headerView; public void setEditText(EditText text) { mEditText = text; } public KhKeyboardView(Activity context, View view) { mContext = context; parentView = view; mNumberKeyboard = new Keyboard(mContext, R.xml.keyboard_numbers); mLetterKeyboard = new Keyboard(mContext, R.xml.keyboard_word); mSymbolKeyboard = new Keyboard(mContext, R.xml.keyboard_symbol); mNumberView = (KeyboardView) parentView.findViewById(R.id.keyboard_view); mLetterView = (KeyboardView) parentView.findViewById(R.id.keyboard_view_2); mNumberView.setKeyboard(mNumberKeyboard); mNumberView.setEnabled( true ); mNumberView.setPreviewEnabled( false ); mNumberView.setOnKeyboardActionListener(listener); mLetterView.setKeyboard(mLetterKeyboard); mLetterView.setEnabled( true ); mLetterView.setPreviewEnabled( true ); mLetterView.setOnKeyboardActionListener(listener); headerView = parentView.findViewById(R.id.keyboard_header); } private KeyboardView.OnKeyboardActionListener listener = new KeyboardView.OnKeyboardActionListener() { /** * 按下,在onKey之前,可以在这里做一些操作,这里让有的没有按下的悬浮提示 * @param primaryCode */ @Override public void onPress( int primaryCode) { Log.d( "primaryCode" , "onPress--" +primaryCode); if (primaryCode == Keyboard.KEYCODE_SHIFT) { mLetterView.setPreviewEnabled( false ); } else if (primaryCode == Keyboard.KEYCODE_DELETE) { mLetterView.setPreviewEnabled( false ); } else if (primaryCode == 32) { mLetterView.setPreviewEnabled( false ); } else { mLetterView.setPreviewEnabled( true ); } } /** * 松开 * @param primaryCode */ @Override public void onRelease( int primaryCode) { Log.d( "primaryCode" , "onRelease--" +primaryCode); } /** * 按下 * @param primaryCode * @param keyCodes */ @Override public void onKey( int primaryCode, int [] keyCodes) { Log.d( "primaryCode" , "onKey--" +primaryCode); try { if (mEditText == null ) return ; Editable editable = mEditText.getText(); int start = mEditText.getSelectionStart(); if (primaryCode == Keyboard.KEYCODE_CANCEL) { // 隐藏键盘 hideKeyboard(); } else if (primaryCode == Keyboard.KEYCODE_DELETE || primaryCode == -35) { // 回退键,删除字符 if (editable != null && editable.length() > 0) { if (start > 0) { editable.delete(start - 1, start); } } } else if (primaryCode == Keyboard.KEYCODE_SHIFT) { // 大小写切换 changeKeyboart(); mLetterView.setKeyboard(mLetterKeyboard); } else if (primaryCode == Keyboard.KEYCODE_MODE_CHANGE) { // 数字与字母键盘互换 if (isNumber) { showLetterView(); showLetterView2(); } else { showNumberView(); } } else if (primaryCode == 90001) { // 字母与符号切换 if (isSymbol) { showLetterView2(); } else { showSymbolView(); } } else { // 输入键盘值 editable.insert(start, Character.toString(( char ) primaryCode)); } } catch (Exception e) { e.printStackTrace(); } } @Override public void onText(CharSequence text) { } @Override public void swipeLeft() { } @Override public void swipeRight() { } @Override public void swipeDown() { } @Override public void swipeUp() { } }; // 字母-符号,显示字母 private void showLetterView2() { if (mLetterView != null ) { isSymbol = false ; mLetterView.setKeyboard(mLetterKeyboard); } } // 字母-符号,显示符号 private void showSymbolView() { try { if (mLetterKeyboard != null ) { isSymbol = true ; mLetterView.setKeyboard(mSymbolKeyboard); } } catch (Exception e) { } } // 数字-字母,显示字母键盘 private void showLetterView() { try { if (mLetterView != null && mNumberView != null ) { isNumber = false ; mLetterView.setVisibility(View.VISIBLE); mNumberView.setVisibility(View.INVISIBLE); } } catch (Exception e) { e.printStackTrace(); } } // 数字-字母, 显示数字键盘 private void showNumberView() { try { if (mLetterView != null && mNumberView != null ) { isNumber = true ; mLetterView.setVisibility(View.INVISIBLE); mNumberView.setVisibility(View.VISIBLE); } } catch (Exception e) { e.printStackTrace(); } } /** * 切换大小写 */ private void changeKeyboart() { List<Keyboard.Key> keyList = mLetterKeyboard.getKeys(); if (isUpper) { // 大写切换小写 isUpper = false ; for (Keyboard.Key key : keyList) { Drawable icon = key.icon; 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() { try { int visibility = mLetterView.getVisibility(); if (visibility == View.VISIBLE) { headerView.setVisibility(View.GONE); mLetterView.setVisibility(View.GONE); } visibility = mNumberView.getVisibility(); if (visibility == View.VISIBLE) { headerView.setVisibility(View.GONE); mNumberView.setVisibility(View.GONE); } } catch (Exception e) { e.printStackTrace(); } } /** * 显示键盘 * * @param editText */ public void showKeyboard(EditText editText) { try { this .mEditText = editText; int visibility = 0; int inputText = mEditText.getInputType(); headerView.setVisibility(View.VISIBLE); switch (inputText) { case InputType.TYPE_CLASS_NUMBER: showNumberView(); break ; case InputType.TYPE_CLASS_PHONE: showNumberView(); break ; case InputType.TYPE_NUMBER_FLAG_DECIMAL: showNumberView(); break ; default : showLetterView(); break ; } } catch (Exception e) { e.printStackTrace(); } } } |
布局
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 | <?xml version= "1.0" encoding= "utf-8" ?> <RelativeLayout xmlns:android= "http://schemas.android.com/apk/res/android" xmlns:tools= "http://schemas.android.com/tools" android:id= "@+id/rl_key" android:layout_width= "match_parent" android:layout_height= "wrap_content" android:background= "#00000000" android:orientation= "vertical" > <View android:id= "@+id/keyboard_back_hide" android:layout_width= "match_parent" android:layout_height= "match_parent" android:visibility= "gone" /> <LinearLayout android:layout_width= "match_parent" android:layout_height= "wrap_content" android:background= "#7d7d7d" android:orientation= "vertical" > <RelativeLayout android:id= "@+id/keyboard_header" android:layout_width= "match_parent" android:layout_height= "wrap_content" android:visibility= "visible" > <TextView android:layout_width= "wrap_content" android:layout_height= "wrap_content" android:layout_centerInParent= "true" android:text= "智能安全加密键盘" android:textColor= "#bfbfbf" android:textSize= "15sp" /> <TextView android:id= "@+id/keyboard_finish" android:layout_width= "wrap_content" android:layout_height= "wrap_content" android:layout_alignParentRight= "true" android:layout_centerVertical= "true" android:layout_marginRight= "10dp" android:padding= "14dp" android:text= "完成" android:textColor= "#ffffff" android:textSize= "15sp" /> </RelativeLayout> <ImageView android:layout_width= "match_parent" android:layout_height= "1dp" android:layout_marginBottom= "10dp" android:background= "#555457" /> <FrameLayout android:id= "@+id/keyboard_layer" android:layout_width= "match_parent" android:layout_height= "wrap_content" > <com.kh.keyboard.CustomKeyboardView android:id= "@+id/keyboard_view" android:layout_width= "match_parent" android:layout_height= "wrap_content" android:background= "#7d7d7d" android:focusable= "true" android:focusableInTouchMode= "true" android:keyBackground= "@drawable/keyboard_number_selector_bg" android:keyPreviewLayout= "@null" android:keyTextColor= "#ffffff" android:visibility= "gone" /> <com.kh.keyboard.CustomKeyboardView android:id= "@+id/keyboard_view_2" android:layout_width= "match_parent" android:layout_height= "wrap_content" android:background= "#7d7d7d" android:focusable= "true" android:focusableInTouchMode= "true" android:keyBackground= "@drawable/keyboard_selector_bg" android:keyPreviewHeight= "90dp" android:keyPreviewLayout= "@layout/keyboard_key_preview_layout" android:keyPreviewOffset= "45dp" android:keyTextColor= "#ffffff" android:visibility= "gone" /> </FrameLayout> </LinearLayout> </RelativeLayout> |
keyPreviewLayout就是点击时键盘按键上的悬浮效果
![](https://images2017.cnblogs.com/blog/827512/201711/827512-20171106160057934-1888243142.jpg)
这里自定义了KeyboardView,因为我需要按钮的背景颜色不一样,而使用keyBackground都是一样的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 | public class CustomKeyboardView extends KeyboardView { private Context context; public CustomKeyboardView(Context context, AttributeSet attrs) { super(context, attrs); this .context = context; } @Override public void onDraw(Canvas canvas) { super.onDraw(canvas); try { List<Keyboard.Key> keys = getKeyboard().getKeys(); for (Keyboard.Key key : keys) { // Log.e("KEY", "Drawing key with code " + key.codes[0]); if (key.codes[0] == -5) { Drawable dr = (Drawable) context.getResources().getDrawable(R.drawable.keyboard_word_del_layerlist); dr.setBounds(key.x, key.y, key.x + key.width, key.y + key.height); dr.draw(canvas); } else if (key.codes[0] == -35) { Drawable dr = (Drawable) context.getResources().getDrawable(R.drawable.keyboard_word_del_layerlist2); dr.setBounds(key.x, key.y, key.x + key.width, key.y + key.height); dr.draw(canvas); } else if (key.codes[0] == -1) { Drawable dr = (Drawable) context.getResources().getDrawable(R.drawable.keyboard_word_shift_layerlist); Drawable dr_da = (Drawable) context.getResources().getDrawable(R.drawable.keyboard_word_shift_layerlist_da); dr.setBounds(key.x, key.y, key.x + key.width, key.y + key.height); dr_da.setBounds(key.x, key.y, key.x + key.width, key.y + key.height); if (KhKeyboardView.isUpper) { dr_da.draw(canvas); } else { dr.draw(canvas); } } else if (key.codes[0] == -2 || key.codes[0] == 90001) { Drawable dr = (Drawable) context.getResources().getDrawable(R.drawable.keyboard_selector_blue_bg); dr.setBounds(key.x, key.y, key.x + key.width, key.y + key.height); dr.draw(canvas); drawText(canvas, key); } else { } } } catch (Exception e) { e.printStackTrace(); } } private void drawText(Canvas canvas, Keyboard.Key key) { try { Rect bounds = new Rect(); Paint paint = new Paint(); paint.setTextAlign(Paint.Align.CENTER); paint.setAntiAlias( true ); paint.setColor(Color.WHITE); if (key.label != null ) { String label = key.label.toString(); Field field; if (label.length() > 1 && key.codes.length < 2) { int labelTextSize = 0; try { field = KeyboardView. class .getDeclaredField( "mLabelTextSize" ); field.setAccessible( true ); labelTextSize = ( int ) field. get ( this ); } catch (NoSuchFieldException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } paint.setTextSize(labelTextSize); paint.setTypeface(Typeface.DEFAULT_BOLD); } else { int keyTextSize = 0; try { field = KeyboardView. class .getDeclaredField( "mLabelTextSize" ); field.setAccessible( true ); keyTextSize = ( int ) field. get ( this ); } catch (NoSuchFieldException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } paint.setTextSize(keyTextSize); paint.setTypeface(Typeface.DEFAULT); } paint.getTextBounds(key.label.toString(), 0, key.label.toString() .length(), bounds); canvas.drawText(key.label.toString(), key.x + (key.width / 2), (key.y + key.height / 2) + bounds.height() / 2, paint); } else 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); } } catch (Exception e) { e.printStackTrace(); } } } |
最后
还需要一个工具类来显示自定义的键盘,这里我使用了dialog
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 | public class KeyBoardDialogUtils implements View.OnClickListener { protected View view; protected Dialog popWindow; protected Activity mContext; private EditText contentView; private List<String> contentList; private KhKeyboardView keyboardUtil; public KeyBoardDialogUtils(Activity mContext) { try { this .mContext = mContext; if (contentList == null ) { contentList = new ArrayList<>(); } if (popWindow == null ) { view = LayoutInflater. from (mContext).inflate(R.layout.keyboard_key_board_popu, null ); view.findViewById(R.id.keyboard_finish).setOnClickListener( this ); view.findViewById(R.id.keyboard_back_hide).setOnClickListener( this ); } popWindow.setContentView(view); popWindow.setCanceledOnTouchOutside( true ); Window mWindow = popWindow.getWindow(); mWindow.setWindowAnimations(R.style.keyboard_popupAnimation); mWindow.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN); mWindow.setGravity(Gravity.BOTTOM | Gravity.FILL_HORIZONTAL); mWindow.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); popWindow.setOnDismissListener( new DialogInterface.OnDismissListener() { @Override public void onDismiss(DialogInterface dialog) { if (contentView != null && contentView.isFocused()) { contentView.clearFocus(); } } }); initView(); } catch (Exception e) { e.printStackTrace(); } } private void initView() { try { if (keyboardUtil == null ) keyboardUtil = new KhKeyboardView(mContext, view); } catch (Exception e) { e.printStackTrace(); } } public void show(final EditText editText) { editText.setFocusable( true ); editText.setFocusableInTouchMode( true ); editText.requestFocus(); popWindow.show(); keyboardUtil.showKeyboard(editText); } public void dismiss() { if (popWindow != null && popWindow.isShowing()) { popWindow.dismiss(); } } @Override public void onClick(View v) { try { int i = v.getId(); if (i == R.id.keyboard_finish) { keyboardUtil.hideKeyboard(); dismiss(); } else if (i == R.id.keyboard_back_hide) { keyboardUtil.hideKeyboard(); dismiss(); } } catch (Exception e) { e.printStackTrace(); } } } |
使用
1 2 3 4 5 6 7 8 | et = (EditText) findViewById(R.id.et); keyBoardDialogUtils = new KeyBoardDialogUtils( this ); et.setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { keyBoardDialogUtils.show(et); } }); |
注意这里点击会先弹出系统键盘,因为弹出键盘会先于keyBoardDialogUtils.show(et)执行,所以设置EditText的focusableInTouchMode="false",在keyutil里我们再把它设为true。
项目源码:
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步