Android自定义安全键盘

在银行APP里经常要自定义键盘,例如实现下面这样的效果

首先在xml文件里定义键盘

<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
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();
        }

    }
}

布局

<?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就是点击时键盘按键上的悬浮效果

这里自定义了KeyboardView,因为我需要按钮的背景颜色不一样,而使用keyBackground都是一样的

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

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();
        }
    }


}

使用

        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。

项目源码:

https://github.com/peiniwan/SafeKeyBoard

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏听雨堂

Android新手之旅(8) ListView的使用

  希望使用ListView来展示信息,每行一个图标,右侧是文字,分为两行布局。经过尝试,这样可以实现: 1、Layout下新建item.xml <?xml...

2486
来自专栏LeoXu的博客

Android中的Fragment使用 原

Android开发中Fragment主要用来提供更灵活的界面构造形式,顾名思义,英语中fragment一词就是片段的意思。我们可以创建一个扩展自Fragmen...

803
来自专栏向治洪

解决ListView嵌套ListView遇到的问题

Listview嵌套会造成的问题主要是子listview的高度错误导致内容不能正常显示完,解决这个问题,我个人第一个想法就是重新计算子listview的高度,代...

2086
来自专栏Android知识点总结

1-VVI-材料设计之-TabLayout上标签

1235
来自专栏cmazxiaoma的架构师之路

Android仿淘宝购物车,玩转电商购物车

1683
来自专栏飞雪无情的博客

Android ListView组件的使用

ListView是Android开发中非常常用的组件,ListView可以用来显示一个列表,我们可以对这个列表操作,比如点击列表要做什么等等。这篇文章主要通过一...

951
来自专栏五毛程序员

使用ActionBar实现Tab导航(快速生成Tab样式)

3533
来自专栏听雨堂

Android新手之旅(8) ListView的使用

  希望使用ListView来展示信息,每行一个图标,右侧是文字,分为两行布局。经过尝试,这样可以实现: 1、Layout下新建item.xml <?xml...

20510
来自专栏Spring相关

Android引入布局

1211
来自专栏三好码农的三亩自留地

Android ListView适配器应该这样写

ViewHolder缓存View的思路我们还是要继续沿用,但是我们希望ViewHolderHelper能够适用所有Adapter,而且我们希望它能有更强大功能,...

1532

扫码关注云+社区

领取腾讯云代金券