android自定义软键盘
本篇文章将会介绍3种android自定义软键盘的应用场景以及代码编写,分别是:普通的自定义软键盘;每次弹出都会改变数字顺序的自定义密码键盘;能与webview交互的自定义密码键盘。以上说的这三种其实本质上没有太大区别,只是细节调用和写法上有略微区别,我们项目里用到是最后一种,但是网上对最后一种的介绍很少,这三种自定义软键盘的介绍顺序也是我的推导顺序,第一种是基础,有了基础后续推导会容易很多,如果你暂时用不到后面的可以只看第一种,有些印象就好。
一、普通基础自定义键盘
1.动态效果图:
2.设计思路:
- 首先在res下创建xml目录,在xml中定义数字键盘和英文键盘;
- 自定义EditText继承EditText并且实现KeyboardView.OnKeyboardActionListener接口用来监听软键盘的点击事件,从而控制软键盘的切换、预览以及输入框的内容变化;
- 判断软键盘弹出是否会遮挡输入框,如果会则使得页面上移到不会遮挡为止,软键盘隐藏时注意页面下移相同距离。
3.软键盘的相关属性介绍:
- key的相关属性:
- row的相关属性:
- KeyboardView的相关属性:
- ASCII码对应表:
4.字母键盘的xml文件编写:
<?xml version="1.0" encoding="utf-8"?> <Keyboard xmlns:android="http://schemas.android.com/apk/res/android" android:keyWidth="8%p" android:keyHeight="7.5%p" android:verticalGap="0px" android:horizontalGap="0px"> <Row android:verticalGap="1%p"> <Key android:codes="113" android:keyLabel="q" android:horizontalGap="1.81%p"> </Key> <Key android:codes="119" android:keyLabel="w" android:horizontalGap="1.81%p"> </Key> <Key android:codes="101" android:keyLabel="e" android:horizontalGap="1.81%p"> </Key> <Key android:codes="114" android:keyLabel="r" android:horizontalGap="1.81%p"> </Key> <Key android:codes="116" android:keyLabel="t" android:horizontalGap="1.81%p"> </Key> <Key android:codes="121" android:keyLabel="y" android:horizontalGap="1.81%p"> </Key> <Key android:codes="117" android:keyLabel="y" android:horizontalGap="1.81%p"> </Key> <Key android:codes="105" android:keyLabel="i" android:horizontalGap="1.81%p"> </Key> <Key android:codes="111" android:keyLabel="o" android:horizontalGap="1.81%p"> </Key> <Key android:codes="112" android:keyLabel="p" android:horizontalGap="1.81%p"> </Key> </Row> <Row android:verticalGap="1%p"> <Key android:codes="97" android:keyLabel="a" android:keyWidth="9%p" android:horizontalGap="5.5%p"> </Key> <Key android:codes="115" android:keyLabel="s" android:keyWidth="9%p" android:horizontalGap="1%p"> </Key> <Key android:codes="100" android:keyLabel="d" android:keyWidth="9%p" android:horizontalGap="1%p"> </Key> <Key android:codes="102" android:keyLabel="f" android:keyWidth="9%p" android:horizontalGap="1%p"> </Key> <Key android:codes="103" android:keyLabel="g" android:keyWidth="9%p" android:horizontalGap="1%p"> </Key> <Key android:codes="104" android:keyLabel="h" android:keyWidth="9%p" android:horizontalGap="1%p"> </Key> <Key android:codes="106" android:keyLabel="j" android:keyWidth="9%p" android:horizontalGap="1%p"> </Key> <Key android:codes="107" android:keyLabel="k" android:keyWidth="9%p" android:horizontalGap="1%p"> </Key> <Key android:codes="108" android:keyLabel="l" android:keyWidth="9%p" android:horizontalGap="1%p"> </Key> </Row> <Row android:verticalGap="1%p"> <Key android:codes="-1" android:keyLabel="大写" android:keyWidth="17%p" android:horizontalGap="1%p"> </Key> <Key android:codes="122" android:keyLabel="z" android:horizontalGap="1%p"> </Key> <Key android:codes="120" android:keyLabel="x" android:horizontalGap="1%p"> </Key> <Key android:codes="99" android:keyLabel="c" android:horizontalGap="1%p"> </Key> <Key android:codes="118" android:keyLabel="v" android:horizontalGap="1%p"> </Key> <Key android:codes="98" android:keyLabel="b" android:horizontalGap="1%p"> </Key> <Key android:codes="110" android:keyLabel="n" android:horizontalGap="1%p"> </Key> <Key android:codes="109" android:keyLabel="m" android:horizontalGap="1%p"> </Key> <Key android:codes="-5" android:isRepeatable="true" android:keyIcon="@mipmap/ic_delete" android:keyWidth="17%p" android:horizontalGap="1%p"> </Key> </Row> <Row> <Key android:codes="-2" android:keyLabel="123" android:keyWidth="20%p" android:horizontalGap="1%p"> </Key> <Key android:codes="32" android:keyLabel="space" android:keyWidth="48%p" android:horizontalGap="5%p"> </Key> <Key android:codes="-4" android:keyLabel="完成" android:keyWidth="20%p" android:horizontalGap="5%p"> </Key> </Row> </Keyboard>
5.字母键盘的xml文件编写:
<?xml version="1.0" encoding="utf-8"?> <Keyboard xmlns:android="http://schemas.android.com/apk/res/android" android:horizontalGap="0px" android:verticalGap="0px" android:keyHeight="7.5%p" android:keyWidth="30%p"> <Row android:verticalGap="1%p"> <Key android:codes="49" android:keyLabel="1" android:horizontalGap="2%p"> </Key> <Key android:codes="50" android:keyLabel="2" android:horizontalGap="2%p"> </Key> <Key android:codes="51" android:keyLabel="3" android:horizontalGap="2%p"> </Key> </Row> <Row android:verticalGap="1%p"> <Key android:codes="52" android:keyLabel="4" android:horizontalGap="2%p"> </Key> <Key android:codes="53" android:keyLabel="5" android:horizontalGap="2%p"> </Key> <Key android:codes="54" android:keyLabel="6" android:horizontalGap="2%p"> </Key> </Row> <Row android:verticalGap="1%p"> <Key android:codes="55" android:keyLabel="7" android:horizontalGap="2%p"> </Key> <Key android:codes="56" android:keyLabel="8" android:horizontalGap="2%p"> </Key> <Key android:codes="57" android:keyLabel="9" android:horizontalGap="2%p"> </Key> </Row> <Row> <Key android:codes="-2" android:keyLabel="abc" android:horizontalGap="2%p"> </Key> <Key android:codes="48" android:keyLabel="0" android:horizontalGap="2%p"> </Key> <Key android:codes="-5" android:isRepeatable="true" android:keyIcon="@mipmap/ic_delete" android:horizontalGap="2%p"> </Key> </Row> </Keyboard>
6.自定义软键盘相关代码及注释:
package com.example.mycustomkeyboard; import android.content.Context; import android.hardware.input.InputManager; import android.inputmethodservice.Keyboard; import android.inputmethodservice.KeyboardView; import android.support.v7.widget.AppCompatEditText; import android.text.Editable; import android.util.AttributeSet; import android.util.Log; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.inputmethod.InputMethodManager; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * Created by zd on 2018/4/2. */ public class KeyBoardEditText extends AppCompatEditText implements KeyboardView.OnKeyboardActionListener {/**数字键盘*/ private Keyboard keyboardNumber; /**字母键盘*/ private Keyboard keyboardLetter; private ViewGroup viewGroup; private KeyboardView keyboardView; /**是否发生键盘切换*/ private boolean changeLetter = false; /**是否为大写*/ private boolean isCapital = false; private int[] arrays = new int[]{Keyboard.KEYCODE_SHIFT, Keyboard.KEYCODE_MODE_CHANGE, Keyboard.KEYCODE_CANCEL, Keyboard.KEYCODE_DONE, Keyboard.KEYCODE_DELETE, Keyboard.KEYCODE_ALT, 32}; private List<Integer> noLists = new ArrayList<>(); private OnKeyboardStateChangeListener listener; public KeyBoardEditText(Context context) {super(context); initEditView(); }public KeyBoardEditText(Context context, AttributeSet attrs) {super(context, attrs); initEditView(); }public KeyBoardEditText(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr); initEditView(); }/**初始化数字和字母键盘*/ private void initEditView() {keyboardNumber = new Keyboard(getContext(), R.xml.keyboard_num); keyboardLetter = new Keyboard(getContext(), R.xml.keyboard_letter); for (int i=0; i<arrays.length; i++) {noLists.add(arrays[i]); }}/** * 设置软键盘刚弹出的时候显示字母键盘还是数字键盘 * @param vg 包裹KeyboardView的ViewGroup * @param kv KeyboardView * @param keyboard_num 是否显示数字键盘 */ public void setKeyboardType (ViewGroup vg, KeyboardView kv, boolean keyboard_num) {viewGroup = vg; keyboardView = kv; if (keyboard_num) {keyboardView.setKeyboard(keyboardNumber); changeLetter = false; } else {keyboardView.setKeyboard(keyboardLetter); changeLetter = true; }//显示预览 keyboardView.setPreviewEnabled(true); //为KeyboardView设置按键监听 keyboardView.setOnKeyboardActionListener(this); }public void setOnKeyBoardStateChangeListener(OnKeyboardStateChangeListener listener) {this.listener = listener; }@Override public void onPress(int primaryCode) {canShowPreview(primaryCode); }/** * 判断是否需要预览Key * * @param primaryCode keyCode */ private void canShowPreview(int primaryCode) {if (noLists.contains(primaryCode)) {keyboardView.setPreviewEnabled(false); } else {keyboardView.setPreviewEnabled(true); }}@Override public void onRelease(int primaryCode) {}@Override public void onKey(int primaryCode, int[] keyCodes) {Editable editable = getText(); int start = getSelectionStart(); switch (primaryCode) {case Keyboard.KEYCODE_DELETE://删除 if (editable != null && editable.length() > 0 && start > 0) {editable.delete(start-1, start); }break; case Keyboard.KEYCODE_MODE_CHANGE://字母键盘与数字键盘切换 changeKeyBoard(!changeLetter); break; case Keyboard.KEYCODE_DONE://完成 keyboardView.setVisibility(View.GONE); viewGroup.setVisibility(GONE); if (listener != null) {listener.hide(); }break; case Keyboard.KEYCODE_SHIFT://大小写切换 changeCapital(!isCapital); keyboardView.setKeyboard(keyboardLetter); break; default:editable.insert(start, Character.toString((char)primaryCode)); break; }}/**切换键盘大小写*/ private void changeCapital(boolean b) {isCapital = b; List<Keyboard.Key> lists = keyboardLetter.getKeys(); for (Keyboard.Key key: lists) {if (key.label != null && isKey(key.label.toString())) {if (isCapital) {key.label = key.label.toString().toUpperCase(); key.codes[0] = key.codes[0] - 32; } else {key.label = key.label.toString().toLowerCase(); key.codes[0] = key.codes[0] + 32; }} else if (key.label != null && key.label.toString().equals("小写")) {key.label = "大写"; } else if (key.label != null && key.label.toString().equals("大写")) {key.label = "小写"; }}}/** * 判断此key是否正确,且存在 * * @param key * @return */ private boolean isKey(String key) {String lowercase = "abcdefghijklmnopqrstuvwxyz"; if (lowercase.indexOf(key.toLowerCase()) > -1) {return true; }return false; }/**切换键盘类型*/ private void changeKeyBoard(boolean b) {changeLetter = b; if (changeLetter) {keyboardView.setKeyboard(keyboardLetter); } else {keyboardView.setKeyboard(keyboardNumber); }}@Override public void onText(CharSequence text) {}@Override public void swipeLeft() {}@Override public void swipeRight() {}@Override public void swipeDown() {}@Override public void swipeUp() {}public interface OnKeyboardStateChangeListener {void show(); void hide(); }@Override public boolean onTouchEvent(MotionEvent event) {hideSystemSoftInput(); if (event.getAction() == MotionEvent.ACTION_UP) {if (keyboardView.getVisibility() != VISIBLE) {keyboardView.setVisibility(VISIBLE); viewGroup.setVisibility(VISIBLE); if (listener != null)listener.show(); }}return true; }@Override public boolean onKeyDown(int keyCode, KeyEvent event) {if (keyCode == KeyEvent.KEYCODE_BACK && (viewGroup.getVisibility() != GONE || keyboardView.getVisibility() != GONE)) {viewGroup.setVisibility(GONE); keyboardView.setVisibility(GONE); if (listener != null)listener.hide(); return true; }return super.onKeyDown(keyCode, event); }@Override protected void onAttachedToWindow() {super.onAttachedToWindow(); hideSystemSoftInput(); }@Override protected void onDetachedFromWindow() {super.onDetachedFromWindow(); hideSystemSoftInput(); }/**隐藏系统软键盘*/ private void hideSystemSoftInput() {InputMethodManager manager = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE); manager.hideSoftInputFromWindow(getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS); } }
7.使用时的页面布局以及调用方法:
- view_keyboard_preview.xml
<?xml version="1.0" encoding="utf-8"?> <TextView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@android:color/holo_red_light" android:textColor="@android:color/white" android:gravity="center" android:textSize="24sp"> </TextView>
- content_keyboard.xml
<?xml version="1.0" encoding="utf-8"?> <android.inputmethodservice.KeyboardView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/view_keyboard" android:background="#999999" android:focusable="true" android:focusableInTouchMode="true" android:keyBackground="@drawable/selector_keyboard_key" android:keyPreviewHeight="64dip" android:keyPreviewLayout="@layout/view_keyboard_preview" android:keyTextColor="@android:color/black" android:keyTextSize="24sp" android:labelTextSize="18sp" android:paddingTop="8dip" android:paddingBottom="8dip" android:shadowColor="#FFFFFF" android:shadowRadius="0.0" android:visibility="gone"> </android.inputmethodservice.KeyboardView>
- activity_main.xml
<?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:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:id="@+id/layout_root" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <View android:layout_width="match_parent" android:layout_height="400dip"></View> <com.example.mycustomkeyboard.KeyBoardEditText android:id="@+id/ed_main" android:layout_width="match_parent" android:layout_height="50dip" android:background="@android:color/holo_orange_dark"/> </LinearLayout> <LinearLayout android:id="@+id/layout_main" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:layout_alignParentBottom="true" android:background="#999999" android:visibility="gone"> <include layout="@layout/content_keyboard"></include> </LinearLayout> </RelativeLayout>
- 调用方法和键盘遮挡输入框时的移动内容代码:
public class MainActivity extends AppCompatActivity {private KeyBoardEditText text; private KeyboardView keyboardView; private LinearLayout layout; private LinearLayout root; private int height = 0; @Override protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); text = (KeyBoardEditText) findViewById(R.id.ed_main); keyboardView = (KeyboardView) findViewById(R.id.view_keyboard); layout = (LinearLayout) findViewById(R.id.layout_main); root = (LinearLayout) findViewById(R.id.layout_root); text.setKeyboardType(layout, keyboardView, true); text.setOnKeyBoardStateChangeListener(new KeyBoardEditText.OnKeyboardStateChangeListener() {@Override public void show() {root.post(new Runnable() {@Override public void run() {int[] pos = new int[2]; //获取编辑框在整个屏幕中的坐标 text.getLocationOnScreen(pos); //编辑框的Bottom坐标和键盘Top坐标的差 height = (pos[1] + text.getHeight()) -(getScreenHeight(MainActivity.this) - keyboardView.getHeight()); if (height > 0) {root.scrollBy(0, height + dp2px(MainActivity.this, 16)); }}}); }@Override public void hide() {if (height > 0) {root.scrollBy(0, -(height + dp2px(MainActivity.this, 16))); }}}); //Log.i("zhangdi", getLngAndLat(this)); }/** * 获得屏幕高度 * * @param context * @return */ public static int getScreenHeight(Context context) {WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); DisplayMetrics displayMetrics = new DisplayMetrics(); wm.getDefaultDisplay().getMetrics(displayMetrics); return displayMetrics.heightPixels; }/** * dp转px * * @param context * @param dpVal * @return dip */ public static int dp2px(Context context, float dpVal) {return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpVal, context.getResources().getDisplayMetrics()); }
二、每次弹出都会改变数字顺序的自定义密码键盘
1.动态效果图:
2.设计思路:
- 在res下创建xml目录,在xml中定义数字键盘;
- 自定义键盘继承KeyboardView并且实现KeyboardView.OnKeyboardActionListener接口,每次显示键盘的时候使键盘上的数字随机排列显示,用接口回调控制输入框显示;
- 每次显示的时候判断软键盘是否会遮挡输入框,如果遮挡则使输入框所在部分内容上移,软键盘隐藏的时候把内容移回原位;
3,遇到的问题:
- 软键盘左右不能都有空隙,如果软键盘左侧有空隙,软键盘宽度不能占满整个屏幕。解决办法:让软键盘左侧有空隙,在软键盘的布局文件外面嵌套一层viewgroup布局,为这个布局设置与软键盘布局相同的底色;
- 如果在xml里设置‘自定义软键盘’和‘完成’,没办法使得‘自定义软键盘’这几个字居中,也控制不好‘完成’的位置。解决方法:把这两项不写在自定义数字键盘的xml文件里,而是写在页面布局嵌套软键盘布局的viewgroup中。
4.数字键盘的xml文件:
<?xml version="1.0" encoding="utf-8"?> <Keyboard xmlns:android="http://schemas.android.com/apk/res/android" android:horizontalGap="0px" android:verticalGap="0px" android:keyHeight="7.5%p" android:keyWidth="30%p"> <Row android:verticalGap="1%p"> <Key android:codes="49" android:keyLabel="1" android:horizontalGap="2.5%p"> </Key> <Key android:codes="50" android:keyLabel="2" android:horizontalGap="2.5%p"> </Key> <Key android:codes="51" android:keyLabel="3" android:horizontalGap="2.5%p"> </Key> </Row> <Row android:verticalGap="1%p"> <Key android:codes="52" android:keyLabel="4" android:horizontalGap="2.5%p"> </Key> <Key android:codes="53" android:keyLabel="5" android:horizontalGap="2.5%p"> </Key> <Key android:codes="54" android:keyLabel="6" android:horizontalGap="2.5%p"> </Key> </Row> <Row android:verticalGap="1%p"> <Key android:codes="55" android:keyLabel="7" android:horizontalGap="2.5%p"> </Key> <Key android:codes="56" android:keyLabel="8" android:horizontalGap="2.5%p"> </Key> <Key android:codes="57" android:keyLabel="9" android:horizontalGap="2.5%p"> </Key> </Row> <Row android:verticalGap="1%p"> <Key android:codes="-5" android:isRepeatable="true" android:keyIcon="@mipmap/ic_delete" android:horizontalGap="2.5%p"> </Key> <Key android:codes="48" android:keyLabel="0" android:horizontalGap="2.5%p"> </Key> <Key android:codes="-3" android:keyLabel="隐藏" android:horizontalGap="2.5%p"> </Key> </Row> </Keyboard>
5.自定义数字键盘代码和注释:
package com.example.mycustomkeyboard; import android.content.Context; import android.inputmethodservice.Keyboard; import android.inputmethodservice.KeyboardView; import android.text.Editable; import android.text.TextUtils; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.util.TypedValue; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; import android.view.inputmethod.InputMethodManager; import android.widget.EditText; import android.widget.TextView; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.Random; /** * Created by zd on 2018/4/24. * 自定义键盘在布局文件中须确保在整体布局的底部,不要和输入框在相同的根布局内 */ public class MyKeyBoardView extends KeyboardView implements KeyboardView.OnKeyboardActionListener {//数字键盘 private Keyboard keyboard; //对应的输入框 private EditText editText; //为防止自定义键盘覆盖输入框,根布局向上的移动高度 private int height = 0; //输入框所在的根布局 private ViewGroup root; //自定义软键盘所在的根布局 private ViewGroup keyBoardRoot; //完成按钮 private TextView complete; public MyKeyBoardView(Context context, AttributeSet attrs) {super(context, attrs); }public MyKeyBoardView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr); }/** * 关联自定义键盘与输入框,以及输入框所在的根布局 * 需要注意此方法需要在输入框的OnTouchListener中当MotionEvent为MotionEvent.ACTION_UP时调用, * 否则无法正确阻止系统软键盘的弹出 * @param et 输入框 * @param root 输入框所在的根布局 */ public void setAttachToEditText(EditText et, ViewGroup root, ViewGroup keyBoardRoot) {if (keyboard == null) {keyboard = new Keyboard(getContext(), R.xml.keyboard_random_num); }this.editText = et; this.root = root; this.keyBoardRoot = keyBoardRoot; complete = keyBoardRoot.findViewById(R.id.complete); complete.setOnClickListener(new OnClickListener() {@Override public void onClick(View v) {hideKeyBoard(); }}); editText.requestFocus(); hideSystemSoftInput(); showMyKeyBoard(); }/**显示自定随机数键盘*/ private void showMyKeyBoard() {randomKeyboardNumber(); setKeyboard(keyboard); setEnabled(true); setPreviewEnabled(false); showResize(); keyBoardRoot.setVisibility(VISIBLE); setVisibility(VISIBLE); setOnKeyboardActionListener(this); }/**根据输入框的底部坐标与自定义键盘的顶部坐标之间的差值height, * 判断自定义键盘是否覆盖住了输入框,如果覆盖则使输入框所在的根布局移动height*/ private void showResize() {root.post(new Runnable() {@Override public void run() {int[] pos = new int[2]; //获取编辑框在整个屏幕中的坐标 editText.getLocationOnScreen(pos); //编辑框的Bottom坐标和键盘Top坐标的差 height = (pos[1] + editText.getHeight()) -(getScreenHeight(getContext()) - keyBoardRoot.getHeight()); if (height > 0) {root.scrollBy(0, height + dp2px(getContext(), 16)); }}}); }/**自定义键盘隐藏时,判断输入框所在的根布局是否向上移动了height,如果移动了则需再移回来*/ private void hideResize() {if (height > 0) {root.scrollBy(0, -(height + dp2px(getContext(), 16))); }}/**获取手机屏幕高度*/ public static int getScreenHeight(Context context) {WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); DisplayMetrics displayMetrics = new DisplayMetrics(); wm.getDefaultDisplay().getMetrics(displayMetrics); return displayMetrics.heightPixels; }/**将px转换成dp*/ public static int dp2px(Context context, float dpVal) {return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpVal, context.getResources().getDisplayMetrics()); }/**打乱数字键盘顺序*/ private void randomKeyboardNumber() {List<Keyboard.Key> keyList = keyboard.getKeys(); // 查找出0-9的数字键 List<Keyboard.Key> newkeyList = new ArrayList<Keyboard.Key>(); for (int i = 0; i < keyList.size(); i++) {if (keyList.get(i).label != null && isNumber(keyList.get(i))) {newkeyList.add(keyList.get(i)); }}// 数组长度 int count = newkeyList.size(); // 结果集 List<KeyModel> resultList = new ArrayList<KeyModel>(); // 用一个LinkedList作为中介 LinkedList<KeyModel> temp = new LinkedList<KeyModel>(); // 初始化temp for (int i = 0; i < count; i++) {temp.add(new KeyModel(48 + i, i + "")); }// 取数 Random rand = new Random(); for (int i = 0; i < count; i++) {int num = rand.nextInt(count - i); resultList.add(new KeyModel(temp.get(num).getCode(), temp.get(num).getLable())); temp.remove(num); }for (int i = 0; i < newkeyList.size(); i++) {newkeyList.get(i).label = resultList.get(i).getLable(); newkeyList.get(i).codes[0] = resultList.get(i).getCode(); }}private class KeyModel {private int code; private String lable; public KeyModel(int code, String lable) {this.code = code; this.lable = lable; }public int getCode() {return code; }public void setCode(int code) {this.code = code; }public String getLable() {return lable; }public void setLable(String lable) {this.lable = lable; }}/**判断key是数字键还是完成键*/ private boolean isNumber(Keyboard.Key key) {if (key.codes[0] < 0) {return false; }return true; }/**隐藏系统键盘*/ private void hideSystemSoftInput() {InputMethodManager manager = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE); manager.hideSoftInputFromWindow(editText.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS); }@Override public void onPress(int primaryCode) {}@Override public void onRelease(int primaryCode) {}@Override public void onKey(int primaryCode, int[] keyCodes) {Editable editable = editText.getText(); //获取焦点光标的所在位置 int start = editText.getSelectionStart(); switch (primaryCode) {case Keyboard.KEYCODE_DELETE://删除 if (editable != null && editable.length() > 0 && start > 0) {editable.delete(start-1, start); }break; case Keyboard.KEYCODE_DONE://完成 break; case Keyboard.KEYCODE_CANCEL://取消、隐藏 hideKeyBoard(); break; default://插入数字 editable.insert(start, Character.toString((char)primaryCode)); }}/**隐藏键盘*/ private void hideKeyBoard() {if (getVisibility() == VISIBLE) {keyBoardRoot.setVisibility(GONE); setVisibility(GONE); hideResize(); }}@Override public void onText(CharSequence text) {}@Override public void swipeLeft() {}@Override public void swipeRight() {}@Override public void swipeDown() {}@Override public void swipeUp() {} }
6.使用时的布局文件和调用方法:
my_keyboard_view.xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/mykeyboard_root" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="#999999" android:layout_alignParentBottom="true" android:visibility="gone"> <TextView android:id="@+id/title" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="自定义软键盘" android:gravity="center" android:textSize="18sp" android:textColor="@android:color/white" android:paddingTop="10dp"/> <TextView android:id="@+id/complete" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="完成" android:textSize="18sp" android:layout_alignParentRight="true" android:layout_marginRight="10dp" android:layout_marginTop="10dp" android:textColor="@color/colorPrimary"/> <com.example.mycustomkeyboard.MyKeyBoardView android:id="@+id/mykeyboard" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="#999999" android:layout_marginTop="10dp" android:keyBackground="@drawable/key_drawable" android:keyTextColor="@android:color/black" android:layout_below="@id/title" android:visibility="gone"/> </RelativeLayout>
activity_main2.xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.example.mycustomkeyboard.Main2Activity"> <LinearLayout android:id="@+id/root" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <EditText android:id="@+id/et1" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="30dp"/> <EditText android:id="@+id/et2" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="30dp"/> <EditText android:id="@+id/et3" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="30dp"/> <EditText android:id="@+id/et4" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="30dp"/> <EditText android:id="@+id/et" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="30dp"/> </LinearLayout> <include layout="@layout/my_keyborad_view"/> </RelativeLayout>
使用方法:
package com.example.mycustomkeyboard; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.MotionEvent; import android.view.View; import android.widget.EditText; import android.widget.LinearLayout; import android.widget.RelativeLayout; public class Main2Activity extends AppCompatActivity implements View.OnTouchListener {private EditText et; private EditText et1; private EditText et2; private EditText et3; private EditText et4; private MyKeyBoardView keyBoardView; private LinearLayout root; private RelativeLayout keyboardRoot; @Override protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState); setContentView(R.layout.activity_main2); et = (EditText) findViewById(R.id.et); et1 = (EditText) findViewById(R.id.et1); et2 = (EditText) findViewById(R.id.et2); et3 = (EditText) findViewById(R.id.et3); et4 = (EditText) findViewById(R.id.et4); root = (LinearLayout) findViewById(R.id.root); keyBoardView = (MyKeyBoardView) findViewById(R.id.mykeyboard); keyboardRoot = (RelativeLayout) findViewById(R.id.mykeyboard_root); et.setOnTouchListener(this); et1.setOnTouchListener(this); et2.setOnTouchListener(this); et3.setOnTouchListener(this); et4.setOnTouchListener(this); }@Override public boolean onTouch(View v, MotionEvent event) {if (event.getAction() == MotionEvent.ACTION_UP) {keyBoardView.setAttachToEditText((EditText) v, root, keyboardRoot); }return true; } }
三、能与webview交互的自定义密码键盘
1.动态效果图:
2.设计思路:
- 与第二种的密码键盘设计思路基本一致,数字键盘的xml文件直接用第二种的就可以,不过软键盘按键控制输入框显示的时候需要调用html中的方法进行显示;
- 判断软件盘是否遮挡输入框时也需要html传递输入框所在位置参数载进行计算,需要注意的是html中传递的输入框位置是相对于页面左上角的,所以需要减去webview的滑动距离,才能计算出输入框相对于屏幕左上角的位置,才能计算出软键盘是否会遮挡输入框。
3.自定义数字键盘代码以及注释:
package com.example.mycustomkeyboard; import android.app.Activity; import android.content.Context; import android.inputmethodservice.Keyboard; import android.inputmethodservice.KeyboardView; import android.text.Editable; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.util.Log; import android.util.TypedValue; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; import android.view.inputmethod.InputMethodManager; import android.webkit.WebView; import android.widget.EditText; import android.widget.TextView; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.Random; /** * Created by zd on 2018/4/24. * 自定义键盘在布局文件中须确保在整体布局的底部,不要和输入框在相同的根布局内 */ public class MyKeyBoardViewWeb extends KeyboardView implements KeyboardView.OnKeyboardActionListener {//数字键盘 private Keyboard keyboard; //为防止自定义键盘覆盖输入框,根布局向上的移动高度 private int height = 0; //输入框所在的根布局 private ViewGroup root; //自定义软键盘所在的根布局 private ViewGroup keyBoardRoot; //完成按钮 private TextView complete; private WebView web; public MyKeyBoardViewWeb(Context context, AttributeSet attrs) {super(context, attrs); }public MyKeyBoardViewWeb(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr); }/** * 关联自定义键盘与输入框,以及输入框所在的根布局 * @param root 输入框所在的根布局 */ public void setAttach(WebView web, int height, ViewGroup root, ViewGroup keyBoardRoot) {if (keyboard == null) {keyboard = new Keyboard(getContext(), R.xml.keyboard_random_num); }this.web = web; this.keyBoardRoot = keyBoardRoot; this.root = root; complete = keyBoardRoot.findViewById(R.id.complete); complete.setOnClickListener(new OnClickListener() {@Override public void onClick(View v) {hideKeyBoard(); }}); hideSystemSoftInput(); showMyKeyBoard(height); }/**显示自定随机数键盘*/ private void showMyKeyBoard(int height) {randomKeyboardNumber(); setKeyboard(keyboard); setEnabled(true); setPreviewEnabled(false); showResize(height); keyBoardRoot.setVisibility(VISIBLE); setVisibility(VISIBLE); setOnKeyboardActionListener(this); }/**根据输入框的底部坐标与自定义键盘的顶部坐标之间的差值height, * 判断自定义键盘是否覆盖住了输入框,如果覆盖则使输入框所在的根布局移动height*/ private void showResize(final int h) {root.post(new Runnable() {@Override public void run() {//获取屏幕高度 int screenHeight = getScreenHeight(getContext()); //获取软键盘高度 int keyHeight = keyBoardRoot.getMeasuredHeight(); //获取编辑框底部距离页面顶部的高度 int etHeight = dp2px(getContext(), h); //获取webview的内容滚动距离 int scrollY = web.getScrollY(); //编辑框底部高度去除webview内容滚动距离获取编辑框底部与屏幕顶部之间的高度 // ,与软键盘与屏幕顶部之间的高度差,如果差值大于0则证明软键盘覆盖住编辑框了,需要内容上移。 height = etHeight - scrollY - (screenHeight - keyHeight); if (height > 0) {root.scrollBy(0, height + dp2px(getContext(), 32)); }}}); }/**自定义键盘隐藏时,判断输入框所在的根布局是否向上移动了height,如果移动了则需再移回来*/ private void hideResize() {if (height > 0) {root.scrollBy(0, -(height + dp2px(getContext(), 32))); }}/**获取手机屏幕高度*/ public static int getScreenHeight(Context context) {WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); DisplayMetrics displayMetrics = new DisplayMetrics(); wm.getDefaultDisplay().getMetrics(displayMetrics); return displayMetrics.heightPixels; }/**将px转换成dp*/ public static int dp2px(Context context, float dpVal) {return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpVal, context.getResources().getDisplayMetrics()); }public static int px2dp(Context context, float dpVal) {return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, dpVal, context.getResources().getDisplayMetrics()); }/**打乱数字键盘顺序*/ private void randomKeyboardNumber() {List<Keyboard.Key> keyList = keyboard.getKeys(); // 查找出0-9的数字键 List<Keyboard.Key> newkeyList = new ArrayList<Keyboard.Key>(); for (int i = 0; i < keyList.size(); i++) {if (keyList.get(i).label != null && isNumber(keyList.get(i))) {newkeyList.add(keyList.get(i)); }}// 数组长度 int count = newkeyList.size(); // 结果集 List<KeyModel> resultList = new ArrayList<KeyModel>(); // 用一个LinkedList作为中介 LinkedList<KeyModel> temp = new LinkedList<KeyModel>(); // 初始化temp for (int i = 0; i < count; i++) {temp.add(new KeyModel(48 + i, i + "")); }// 取数 Random rand = new Random(); for (int i = 0; i < count; i++) {int num = rand.nextInt(count - i); resultList.add(new KeyModel(temp.get(num).getCode(), temp.get(num).getLable())); temp.remove(num); }for (int i = 0; i < newkeyList.size(); i++) {newkeyList.get(i).label = resultList.get(i).getLable(); newkeyList.get(i).codes[0] = resultList.get(i).getCode(); }}private class KeyModel {private int code; private String lable; public KeyModel(int code, String lable) {this.code = code; this.lable = lable; }public int getCode() {return code; }public void setCode(int code) {this.code = code; }public String getLable() {return lable; }public void setLable(String lable) {this.lable = lable; }}/**判断key是数字键还是完成键*/ private boolean isNumber(Keyboard.Key key) {if (key.codes[0] < 0) {return false; }return true; }/**隐藏系统键盘*/ public void hideSystemSoftInput() {InputMethodManager manager = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE); manager.hideSoftInputFromWindow(((Activity)getContext()).getCurrentFocus().getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS); }@Override public void onPress(int primaryCode) {}@Override public void onRelease(int primaryCode) {}@Override public void onKey(int primaryCode, int[] keyCodes) {switch (primaryCode) {case Keyboard.KEYCODE_DELETE://删除 web.loadUrl("javascript:del()"); break; case Keyboard.KEYCODE_DONE://完成 break; case Keyboard.KEYCODE_CANCEL://取消、隐藏 hideKeyBoard(); break; default://插入数字 String content = Character.toString((char)primaryCode); web.loadUrl("javascript:insert("+content+")"); }}/**隐藏键盘*/ private void hideKeyBoard() {if (getVisibility() == VISIBLE) {keyBoardRoot.setVisibility(GONE); setVisibility(GONE); hideResize(); }}@Override public void onText(CharSequence text) {}@Override public void swipeLeft() {}@Override public void swipeRight() {}@Override public void swipeDown() {}@Override public void swipeUp() {} }
4.使用的html相关代码:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0 , maximum-scale=1.0, user-scalable=0"> <title>Show.html</title> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="this is my page"> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <!--<link rel="stylesheet" type="text/css" href="./styles.css">--> <script type="text/javascript"> var name; function init(s) {name = s;setLoseFocus();sub(); }function setFocus() {//设置焦点var et = document.getElementById(name);et.focus(); }function setLoseFocus() {//取消焦点var et = document.getElementById(name);et.blur(); }function sub(){var et = document.getElementById(name);var height = getPos(et);console.info(height);window.demo.showInput(height);//调用android方法,弹出软键盘<!--console.info("哈哈");--> }function getPos(o) //取元素坐标 {var y = o.offsetTop;y += o.offsetHeight;return y; }function del() {//删除var str = document.getElementById(name).value;var strNew = str.substring(0,str.length-1);document.getElementById(name).value = strNew; }function insert(str) {//插入var s = document.getElementById(name).value;var strNew = s + str;document.getElementById(name).value = strNew; } </script> </head> <body> <br/> <br/> <br/> <input type="text" id="txt1" οnclick="init('txt1')" value=""> <br/> <br/> <br/> <br/> <br/> <br/> <br/> <br/> <br/> <br/> <br/> <br/> <br/> <br/> <input type="text" id="txt2" οnclick="init('txt2')" value=""> <br/> <br/> <br/> <br/> <br/> <br/> <br/> <br/> <br/> <br/> <br/> <br/> <br/> <br/> <br/> <br/> <br/> <br/> <br/> <br/> <input type="text" id="txt" οnclick="init('txt')" value=""> </body> </html>
5.使用时的布局以及调用方法:
my_keyboard_view_web.xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/mykeyboard_root" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="#999999" android:layout_alignParentBottom="true" android:visibility="gone"> <TextView android:id="@+id/title" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="自定义软键盘" android:gravity="center" android:textSize="18sp" android:textColor="@android:color/white" android:paddingTop="10dp"/> <TextView android:id="@+id/complete" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="完成" android:textSize="18sp" android:layout_alignParentRight="true" android:layout_marginRight="10dp" android:layout_marginTop="10dp" android:textColor="@color/colorPrimary"/> <com.example.mycustomkeyboard.MyKeyBoardViewWeb android:id="@+id/mykeyboard" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="#999999" android:layout_marginTop="10dp" android:keyBackground="@drawable/key_drawable" android:keyTextColor="@android:color/black" android:layout_below="@id/title" android:visibility="gone"/> </RelativeLayout>
activity_webview.xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.example.mycustomkeyboard.WebviewActivity"> <RelativeLayout android:id="@+id/root" android:layout_width="match_parent" android:layout_height="match_parent"> <WebView android:id="@+id/web" android:layout_width="match_parent" android:layout_height="match_parent"/> </RelativeLayout> <include layout="@layout/my_keyborad_view_web"/> </RelativeLayout>
调用方法:
package com.example.mycustomkeyboard; import android.app.Activity; import android.os.Bundle; import android.text.Editable; import android.view.View; import android.webkit.JavascriptInterface; import android.webkit.WebView; import android.widget.EditText; import android.widget.LinearLayout; import android.widget.RelativeLayout; public class WebviewActivity extends Activity {private WebView web; private MyKeyBoardViewWeb keyBoardView; private RelativeLayout root; private RelativeLayout keyboardRoot; @Override protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState); setContentView(R.layout.activity_webview); root = (RelativeLayout) findViewById(R.id.root); keyBoardView = (MyKeyBoardViewWeb) findViewById(R.id.mykeyboard); keyboardRoot = (RelativeLayout) findViewById(R.id.mykeyboard_root); web = findViewById(R.id.web); web.getSettings().setJavaScriptEnabled(true); web.getSettings().setDomStorageEnabled(true); web.addJavascriptInterface(new DemoJavaScriptInterface(),"demo"); web.loadUrl("file:///android_asset/index.html"); }public class DemoJavaScriptInterface {@JavascriptInterface public void showInput(final int height) {runOnUiThread(new Runnable() {@Override public void run() {if (keyBoardView.getVisibility() != View.VISIBLE) {keyBoardView.setAttach(web, height, root, keyboardRoot); }}}); }} }
最后附上demo下载地址,使用android studio添加moudle可以直接运行看看效果: 下载demo
android自定义软键盘相关推荐
- android自定义系统键盘,Android自定义软键盘
[实例简介] Android自定义软键盘 [实例截图] [核心代码] keydemo └── keydemo ├── AndroidManifest.xml ├── bin │ ├── class ...
- Android 自定义软键盘实现 数字九宫格
前言 最近项目在对接美团外卖功能 实现外面小哥凭取货码取货 对接完功能后 用户反馈 弹出的软键盘 很难输入 数字太小了 大概是下面这种显示方式 需求 组长说 要不搞一个自定义软键盘吧 数字搞大点 方便 ...
- Android自定义软键盘输入法,隐藏系统输入法显示光标的实现
android实现自定义软键盘,先上图看效果,效果基本上是仿ios输入法实现的 这里是实现隐藏系统输入法,同时让EditText能获取光标的代码部分(通过反射调用): <span style=& ...
- Android自定义软键盘样式:字母、数字、标点三种切换
先看效果图: 1.在需要的调用软键盘的activity_mian.xml中加入键盘控件 <!--自定义键盘控件--> <RelativeLayoutandroid:layout_wi ...
- android自动软键盘,Android自定义软键盘
MyKeyboard Android自定义键盘的使用 实现步骤 第一步: 1.新建一个xml文件夹放在res目录下面,然后新建xml文件:money_keyboard.xml 2.然后在XML文件中添 ...
- Android 自定义软键盘实现 数字九宫格,2021年Android常见面试题目
实现效果GIF 实现代码 自定义View 一个NineNumericKeyboardView /** Author by Lyu Date on 2021/5/26-19:55 Description ...
- Android 自定义软键盘实现
module链接:https://download.csdn.net/download/meixi_android/10652565 compile project(':edlibrary') , ' ...
- android 自定义软键盘
哦然间发现了android.inputmethodservice.Keyboard类,即android可以自定义键盘类,做了一个简单例子供大家参考, 首先看看效果图: 键盘内容布局:keyconten ...
- android自定义键盘开源,Android自定义软键盘的设计与实现代码
偶然间发现了Android.inputmethodservice.Keyboard类,即android可以自定义键盘类,做了一个简单例子供大家参考. 效果如下: 先看界面布局文件 android:la ...
- Android自定义软键盘的实现
先看界面布局文件 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:and ...
最新文章
- IOS的钥匙串,确保本地隐私数据的安全
- valgrind——hisi平台valgrind
- Android 高级自定义View实战
- 八城联动丨神策 2020 数据驱动用户大会「合肥站」邀您免费参会!
- Vue 2017 现状与展望 | 视频+PPT+速记快速回顾
- 用vim写php,Vim/Neovim中配置PHP的代码调试
- 获取项目中的文件流InputStream
- java8的新特性详解-----------Lamda表达式
- 标题决胜武器:标题常用的14个套路
- Mybatis之分表设计与分表插入
- 面试中的字符串问题 (1)
- Visual studio2010 编写dll 及 使用dll
- 全新SQL Server教程
- AT89C51单片机8位竞赛抢答器_倒计时可调仿真设计
- 深度卷积神经网络(AlexNet)
- 新版游戏陪玩约玩APP源码 多人连麦聊天/语音直播社交双端APP源代码 附搭建教程文档
- PDF导出图片-python
- Linux RHEL/Ubuntu安装教程
- echarts将x轴展示在图标上方
- matlab通过数据进行曲线拟合 导出公式
热门文章
- 学生信息管理系统(附运行效果图和源码下载)分页技术(后台封装json数据传递到前端显示,动态分页等)(Mybatis,json,ajax,jQuery实用整合示例)
- Javascript:模拟ztree侧边栏的回收
- java 数据库按钮跳转_java,数据库的连接及基本操作
- 华为算法精英赛(题3:概率计算)
- ubuntu16.04 安装完显卡驱动后分辨率固定640x480 解决
- 吴恩达教授机器学习课程笔记【九】- k均值聚类算法
- DB2 DatabaseMetadata类的使用
- 论文阅读笔记(四)——ESPNetV2:A Light-weight Power Efficient and General Purpose Convolutional Neural Network
- Single-Shot Calibration:基于全景基础设施的多相机和多激光雷达之间的外参标定(ICRA2021)...
- opencv鼠标回调函数实现ROI区域像素值相同化