android开发(45) 自定义软键盘(输入法)
概述
在项目开发中遇到一个需求,”只要数字键盘的输入,仅仅有大写字母的输入,某些输入法总是会提示更新,弹出广告等“,使得我们需要自定义输入。
关联到的知识
- KeyboardView 一个视图对象,展示了键盘。它需要关联到一个 Keyboard对象才能展示。
- Keyboard 键盘对象,通过加载xml的配置获得键盘的排列。
- xml 文件键盘描述 一个xml文件,放置在 xml 资源文件夹下,描述了 显示的键盘按钮,和排列,键盘宽度和高度等。
具体实现
准备xml键盘描述文件
在xml文件夹下创建文件,下面的代码中使用 “ 33%p” 这样的单位指定一定的 百分比,以适配屏幕,详细内容如下:
<?xml version="1.0" encoding="UTF-8"?><!-- 数字键盘 --> <Keyboard xmlns:android="http://schemas.android.com/apk/res/android" android:horizontalGap="0dp" android:keyHeight="61dp" android:keyWidth="33%p" android:verticalGap="0dp"> <Row> <Key android:codes="49" android:keyEdgeFlags="left" android:keyLabel="1" /> <Key android:codes="50" android:keyLabel="2" /> <Key android:codes="51" android:keyLabel="3" /> </Row> <Row> <Key android:codes="52" android:keyEdgeFlags="left" android:keyLabel="4" /> <Key android:codes="53" android:keyLabel="5" /> <Key android:codes="54" android:keyLabel="6" /> </Row> <Row> <Key android:codes="55" android:keyEdgeFlags="left" android:keyLabel="7" /> <Key android:codes="56" android:keyLabel="8" /> <Key android:codes="57" android:keyLabel="9" /> </Row> <Row> <Key android:codes="48" android:keyEdgeFlags="left" android:keyLabel="0" /> <Key android:codes="-5" android:isRepeatable="true" android:keyIcon="@drawable/keyboard_delete" android:keyWidth="66%p" /> </Row> </Keyboard>
创建Keyboard对象
要先配置好xml文件,在构造方法里传入上面的xml文件
this.keyboard = new Keyboard(mActivity, R.xml.small_keyboard);
构造KeyboardView
keyboardView 对象可以在 xml 中描述,类似下面这样
<android.inputmethodservice.KeyboardView android:id="@+id/keyboard_view" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center" android:background="@android:color/transparent" android:focusable="true" android:focusableInTouchMode="true" android:keyBackground="@drawable/keyboard_key" android:keyTextColor="@color/white" android:keyTextSize="@dimen/sp_32" android:visibility="visible" />
获得 KeyboardView并进行配置,需要关联到具体的 keyboard 对象
KeyboardView keyboardView = (KeyboardView) viewContainer.findViewById(R.id.keyboard_view); this.keyboardView = keyboardView; this.keyboardView.setKeyboard(keyboard); this.keyboardView.setEnabled(true); this.keyboardView.setPreviewEnabled(false); this.keyboardView.setOnKeyboardActionListener(listener2);
隐藏系统自带的键盘
根据android系统的版本的不同,有不同的方法,需要利用反射,见代码:
/** * 隐藏系统键盘 * * @param editText */ public static void hideSystemSofeKeyboard(EditText editText) { int sdkInt = Build.VERSION.SDK_INT; if (sdkInt >= 11) { try { Class<EditText> cls = EditText.class; Method setShowSoftInputOnFocus; setShowSoftInputOnFocus = cls.getMethod("setShowSoftInputOnFocus", boolean.class); setShowSoftInputOnFocus.setAccessible(true); setShowSoftInputOnFocus.invoke(editText, false); } catch (SecurityException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } else { editText.setInputType(InputType.TYPE_NULL); } }
从底部弹出键盘
输入法需要从页面底部向上弹出,需要一个过渡动画,android每个页面都有一个window,window包含了一个getDecorView 根视图,我们要把键盘的视图添加到这个根视图下,配合动画出现键盘。
public void showSoftKeyboard() { if (viewContainer == null) { viewContainer = mActivity.getLayoutInflater().inflate(R.layout.keyboardview_layout, null); } else { if (viewContainer.getParent() != null) return; } FrameLayout frameLayout = (FrameLayout) mActivity.getWindow().getDecorView(); KeyboardView keyboardView = (KeyboardView) viewContainer.findViewById(R.id.keyboard_view); this.keyboardView = keyboardView; this.keyboardView.setKeyboard(keyboard); this.keyboardView.setEnabled(true); this.keyboardView.setPreviewEnabled(false); this.keyboardView.setOnKeyboardActionListener(listener2); FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.WRAP_CONTENT); lp.gravity = Gravity.BOTTOM; frameLayout.addView(viewContainer, lp); //viewContainer.setVisibility(View.GONE); viewContainer.setAnimation(AnimationUtils.loadAnimation(mActivity, R.anim.down_to_up)); }
完整的代码如下:
package vir56k.democustomkeyboard; import android.app.Activity; import android.inputmethodservice.Keyboard; import android.inputmethodservice.KeyboardView; import android.inputmethodservice.KeyboardView.OnKeyboardActionListener; import android.os.Build; import android.text.Editable; import android.text.InputType; import android.text.TextUtils; import android.view.Gravity; import android.view.View; import android.view.ViewGroup; import android.view.animation.AnimationUtils; import android.widget.EditText; import android.widget.FrameLayout; import java.lang.reflect.Method; public class PopupKeyboardUtil { private Activity mActivity; private KeyboardView keyboardView; private Keyboard keyboard;// 全键盘包括数字和字母 private EditText editText1; public PopupKeyboardUtil(Activity mActivity) { this.mActivity = mActivity; this.keyboard = new Keyboard(mActivity, R.xml.small_keyboard); } public void attachTo(EditText editText, boolean isAuto) { this.editText1 = editText; hideSystemSofeKeyboard(this.editText1); setAutoShowOnFocs(isAuto); } public void setAutoShowOnFocs(boolean enable) { if (editText1 == null) return; if (enable) editText1.setOnFocusChangeListener(onFocusChangeListener1); else editText1.setOnFocusChangeListener(null); } View.OnFocusChangeListener onFocusChangeListener1 = new View.OnFocusChangeListener() { @Override public void onFocusChange(View v, boolean hasFocus) { if (hasFocus) showSoftKeyboard(); else hideSoftKeyboard(); } }; View viewContainer; public void showSoftKeyboard() { if (viewContainer == null) { viewContainer = mActivity.getLayoutInflater().inflate(R.layout.keyboardview_layout, null); } else { if (viewContainer.getParent() != null) return; } FrameLayout frameLayout = (FrameLayout) mActivity.getWindow().getDecorView(); KeyboardView keyboardView = (KeyboardView) viewContainer.findViewById(R.id.keyboard_view); this.keyboardView = keyboardView; this.keyboardView.setKeyboard(keyboard); this.keyboardView.setEnabled(true); this.keyboardView.setPreviewEnabled(false); this.keyboardView.setOnKeyboardActionListener(listener2); FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.WRAP_CONTENT); lp.gravity = Gravity.BOTTOM; frameLayout.addView(viewContainer, lp); //viewContainer.setVisibility(View.GONE); viewContainer.setAnimation(AnimationUtils.loadAnimation(mActivity, R.anim.down_to_up)); } public void hideSoftKeyboard() { if (viewContainer != null && viewContainer.getParent() != null) { ((ViewGroup) viewContainer.getParent()).removeView(viewContainer); } } public boolean isShowing() { if (viewContainer == null) return false; return viewContainer.getVisibility() == View.VISIBLE; } /** * 隐藏系统键盘 * * @param editText */ public static void hideSystemSofeKeyboard(EditText editText) { int sdkInt = Build.VERSION.SDK_INT; if (sdkInt >= 11) { try { Class<EditText> cls = EditText.class; Method setShowSoftInputOnFocus; setShowSoftInputOnFocus = cls.getMethod("setShowSoftInputOnFocus", boolean.class); setShowSoftInputOnFocus.setAccessible(true); setShowSoftInputOnFocus.invoke(editText, false); } catch (SecurityException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } else { editText.setInputType(InputType.TYPE_NULL); } } private OnKeyboardActionListener listener2 = new OnKeyboardActionListener() { @Override public void swipeUp() { } @Override public void swipeRight() { } @Override public void swipeLeft() { } @Override public void swipeDown() { } @Override public void onText(CharSequence text) { } @Override public void onRelease(int primaryCode) { } @Override public void onPress(int primaryCode) { } @Override public void onKey(int primaryCode, int[] keyCodes) { if (editText1 != null) { keyCode_delect(primaryCode, editText1); } keyboardView.postInvalidate(); } }; /** * 判断回退键 和大小写切换 * * @param primaryCode * @param edText */ private void keyCode_delect(int primaryCode, EditText edText) { Editable editable = edText.getText(); int start = edText.getSelectionStart(); if (primaryCode == Keyboard.KEYCODE_DELETE) {// 回退 if (edText.hasFocus()) { if (!TextUtils.isEmpty(editable)) { if (start > 0) { editable.delete(start - 1, start); } } } } else if (primaryCode == Keyboard.KEYCODE_SHIFT) {// 大小写切换 keyboardView.setKeyboard(keyboard); } else { if (edText.hasFocus()) { editable.insert(start, Character.toString((char) primaryCode)); } } } }
功能完成后,具体调用的演示:
package vir56k.democustomkeyboard; import android.app.Activity; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.EditText; public class MainActivity extends AppCompatActivity { EditText edittext1; PopupKeyboardUtil smallKeyboardUtil; private View viewContainer; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); edittext1 = (EditText) findViewById(R.id.edittext1); smallKeyboardUtil = new PopupKeyboardUtil(self()); smallKeyboardUtil.attachTo(edittext1, false); //smallKeyboardUtil.setAutoShowOnFocs(false); } public void onClickView(View view) { if (view.getId() == R.id.btn1) smallKeyboardUtil.showSoftKeyboard(); if (view.getId() == R.id.btn2) smallKeyboardUtil.hideSoftKeyboard(); } private Activity self() { return this; } }
完整的代码下载:
https://github.com/vir56k/demo/tree/master/demo.customkeyboard