Android 自定义数字键盘

先来看下截图

 

 

有时需要输入验证码或者数字密码的地方,为了安全和便捷,应用内通常会自定义一个数字键盘,来帮助用户完成输入。

那么我们来自己动手撸一个键盘出来;

 

1.首先在res目录里新建xml目录,然后在xml目录中新建num_keyboard.xml:

<?xml version="1.0" encoding="utf-8"?>
<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
    android:horizontalGap="1dp"
    android:keyHeight="8%p"
    android:keyWidth="33.33333%p"
    android:verticalGap="1dp">

    <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="-10"
            android:keyLabel="取消" />
        <Key
            android:codes="48"
            android:keyLabel="0" />

        <Key
            android:codes="-5"
            android:keyIcon="@android:color/transparent" />
    </Row>

</Keyboard>

上面代码中,定义了KeyBoard 的按键排位和键值,keyLabl等参数;

 

 

2.自定义属性

在res/values/attrs.xml   中新增如下内容, 没有的话就创建这个文件

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <declare-styleable name="NumberKeyboardView">

        <!-- 删除按键的图标 -->
        <attr name="xnkv_deleteDrawable" format="reference" />

        <!-- 删除按键图标的宽度 -->
        <attr name="xnkv_deleteWidth" format="dimension|reference" />

        <!-- 删除按键图标的高度 -->
        <attr name="xnkv_deleteHeight" format="dimension|reference" />

        <!-- 删除按键图标的颜色 -->
        <attr name="xnkv_deleteBackgroundColor" format="color|reference" />

    </declare-styleable>

</resources>

 

3.自定义KeyBoardView



import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.inputmethodservice.Keyboard;
import android.inputmethodservice.KeyboardView;
import android.util.AttributeSet;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;


/**
 * 自定义数字键盘
 * kpl
 */
public class NumberKeyboardView extends KeyboardView implements KeyboardView.OnKeyboardActionListener {

    // 用于区分左下角空白的按键
    private static final int KEYCODE_EMPTY = -10;

    private int mDeleteWidth;
    private int mDeleteHeight;
    private int mDeleteBackgroundColor;
    private Drawable mDeleteDrawable;
    private Rect mDeleteDrawRect;

    private IOnKeyboardListener mOnKeyboardListener;
    private boolean showCancel = true;

    public NumberKeyboardView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs, 0);
    }

    public NumberKeyboardView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs, defStyleAttr);
    }

    private void init(Context context, AttributeSet attrs, int defStyleAttr) {
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.NumberKeyboardView,
                defStyleAttr, 0);
        mDeleteDrawable = a.getDrawable(R.styleable.NumberKeyboardView_xnkv_deleteDrawable);
        mDeleteBackgroundColor = a.getColor(
                R.styleable.NumberKeyboardView_xnkv_deleteBackgroundColor, Color.TRANSPARENT);
        mDeleteWidth = a.getDimensionPixelOffset(R.styleable.NumberKeyboardView_xnkv_deleteWidth,
                -1);
        mDeleteHeight = a.getDimensionPixelOffset(R.styleable.NumberKeyboardView_xnkv_deleteHeight,
                -1);
        a.recycle();

        // 设置软键盘按键的布局
        Keyboard keyboard = new Keyboard(context, R.xml.keybord_num_layout);
        setKeyboard(keyboard);

        setEnabled(true);
        setPreviewEnabled(false); // 设置按键没有点击放大镜显示的效果
        setOnKeyboardActionListener(this);
    }

    @Override
    public void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        // 遍历所有的按键
        List<Keyboard.Key> keys = getKeyboard().getKeys();
        for (Keyboard.Key key : keys) {
            // 如果是左下角空白的按键,重画按键的背景
            if (key.codes[0] == KEYCODE_EMPTY) {
                if (showCancel) {
                    key.label = "取消";
                } else {
                    drawKeyBackground(key, canvas, mDeleteBackgroundColor);
                }
            }
            // 如果是右下角的删除按键,重画按键的背景,并且绘制删除图标
            else if (key.codes[0] == Keyboard.KEYCODE_DELETE) {
                drawKeyBackground(key, canvas, mDeleteBackgroundColor);
                drawDeleteButton(key, canvas);
            }
        }
    }

    // 绘制按键的背景
    private void drawKeyBackground(Keyboard.Key key, Canvas canvas, int color) {
        ColorDrawable drawable = new ColorDrawable(color);
        drawable.setBounds(key.x, key.y, key.x + key.width, key.y + key.height);
        drawable.draw(canvas);
    }


    public void setShowCancelButton(boolean isShow) {
        this.showCancel = isShow;
        invalidate();
    }


    /**
     * 设置返回按键图标
     *
     * @param icon
     */
    public void setBackKeyIcon(Drawable icon) {
        mDeleteDrawable = icon;
        invalidate();
    }

    // 绘制删除按键
    private void drawDeleteButton(Keyboard.Key key, Canvas canvas) {
        if (mDeleteDrawable == null) {
            return;
        }

        // 计算删除图标绘制的坐标
        if (mDeleteDrawRect == null || mDeleteDrawRect.isEmpty()) {
            int drawWidth, drawHeight;
            int intrinsicWidth = mDeleteDrawable.getIntrinsicWidth();
            int intrinsicHeight = mDeleteDrawable.getIntrinsicHeight();

            if (mDeleteWidth > 0 && mDeleteHeight > 0) {
                drawWidth = mDeleteWidth;
                drawHeight = mDeleteHeight;
            } else if (mDeleteWidth > 0 && mDeleteHeight <= 0) {
                drawWidth = mDeleteWidth;
                drawHeight = drawWidth * intrinsicHeight / intrinsicWidth;
            } else if (mDeleteWidth <= 0 && mDeleteHeight > 0) {
                drawHeight = mDeleteHeight;
                drawWidth = drawHeight * intrinsicWidth / intrinsicHeight;
            } else {
                drawWidth = intrinsicWidth;
                drawHeight = intrinsicHeight;
            }

            // 限制图标的大小,防止图标超出按键
            if (drawWidth > key.width) {
                drawWidth = key.width;
                drawHeight = drawWidth * intrinsicHeight / intrinsicWidth;
            }
            if (drawHeight > key.height) {
                drawHeight = key.height;
                drawWidth = drawHeight * intrinsicWidth / intrinsicHeight;
            }

            // 获取删除图标绘制的坐标
            int left = key.x + (key.width - drawWidth) / 2;
            int top = key.y + (key.height - drawHeight) / 2;
            mDeleteDrawRect = new Rect(left, top, left + drawWidth, top + drawHeight);
        }

        // 绘制删除的图标
        if (mDeleteDrawRect != null && !mDeleteDrawRect.isEmpty()) {
            mDeleteDrawable.setBounds(mDeleteDrawRect.left, mDeleteDrawRect.top,
                    mDeleteDrawRect.right, mDeleteDrawRect.bottom);
            mDeleteDrawable.draw(canvas);
        }
    }

    @Override
    public void onKey(int primaryCode, int[] keyCodes) {
        // 处理按键的点击事件
        // 点击了删除按键
        if (primaryCode == Keyboard.KEYCODE_DELETE) {
            if (mOnKeyboardListener != null)
                mOnKeyboardListener.onDeleteKeyEvent();
        }
        // 点击了数字按键
        else if (primaryCode != KEYCODE_EMPTY) {
            if (mOnKeyboardListener != null) {
                mOnKeyboardListener.onInsertKeyEvent(Character.toString(
                        (char) primaryCode));
            }
        } else if (showCancel && primaryCode == KEYCODE_EMPTY) {
            if (mOnKeyboardListener != null)
                mOnKeyboardListener.onCancel();
        }
    }

    // 0-9 数字的 Character 值
    private final List<Character> keyCodes = Arrays.asList('0', '1', '2', '3', '4', '5', '6', '7','8','9');

    /**
     * 随机打乱数字键盘上键位的排列顺序。
     */
    public void shuffleKeyboard() {
        Keyboard keyboard = getKeyboard();
        if (keyboard != null && keyboard.getKeys() != null && keyboard.getKeys().size() > 0) {

            Collections.shuffle(keyCodes); // 随机排序数字

            // 遍历所有的按键
            List<Keyboard.Key> keys = getKeyboard().getKeys();
            int index = 0;
            for (Keyboard.Key key : keys) {
                // 如果按键是数字
                if (key.codes[0] != KEYCODE_EMPTY && key.codes[0] != Keyboard.KEYCODE_DELETE) {
                    char code = keyCodes.get(index++);
                    key.codes[0] = code;
                    key.label = Character.toString(code);
                }
            }
            setKeyboard(keyboard);
        }
    }

    @Override
    public void onPress(int primaryCode) {

    }

    @Override
    public void onRelease(int primaryCode) {

    }

    @Override
    public void onText(CharSequence text) {

    }

    @Override
    public void swipeLeft() {

    }

    @Override
    public void swipeRight() {

    }

    @Override
    public void swipeDown() {

    }

    @Override
    public void swipeUp() {

    }

    /**
     * 设置键盘的监听事件。
     *
     * @param listener 监听事件
     */
    public void setIOnKeyboardListener(IOnKeyboardListener listener) {
        this.mOnKeyboardListener = listener;
    }

    /**
     * 键盘的监听事件。
     */
    public interface IOnKeyboardListener {

        /**
         * 点击数字按键。
         *
         * @param text 输入的数字
         */
        void onInsertKeyEvent(String text);

        /**
         * 点击了删除按键。
         */
        void onDeleteKeyEvent();

        /**
         *  点击了取消键
         */
        void onCancel();

    }
}

 

4.自定义包裹键盘的layout

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#00000000"
    android:orientation="vertical"
    >


    <View
        android:id="@+id/view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:background="#00000000" />


    <View
        android:layout_width="match_parent"
        android:layout_height="1px"
        android:background="#33000000"
        />



    <com.kpl.test.numkeybordlib.NumberKeyboardView xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/view_keyboard"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:background="#b0b0b0"
        android:focusable="true"
        android:focusableInTouchMode="true"
        android:keyBackground="@drawable/selector_key_background"
        android:keyTextColor="#000000"
        android:shadowColor="@android:color/transparent"
        android:shadowRadius="0"
        app:xnkv_deleteBackgroundColor="#d2d2d2"
        app:xnkv_deleteDrawable="@drawable/back"
        app:xnkv_deleteWidth="22dp" />
</LinearLayout>

当然,布局是一个match_parent 的LinearLayout ,目的是为了使键盘能该在满界面的最前端, 除了键盘view , 还有两个view控件,其中一个完全透明, 用来铺满除键盘布局外剩余的屏幕空间;

android:keyBackground="@drawable/selector_key_background"

这里给键盘设置了状态选择器当背景,代码如下

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:drawable="@android:color/holo_blue_dark" android:state_pressed="true" />
    <item android:drawable="@android:color/white" />

</selector>

其他的还有几个自定义的属性,用来配置删除按钮;

 

5.NumberKeyboardLayout   对键盘的封装, 外部使用键盘的时候,只需要使用这个对象即可;



import android.content.Context;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.LinearLayout;

/**
*        kpl  自定义数字键盘 ,对外使用
*/
public class NumberKeyboardLayout extends LinearLayout {


    private NumberKeyboardView keyboardView;
    private View view;

    public NumberKeyboardLayout(Context context) {
        super(context);
        init(context);
    }

    public NumberKeyboardLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    public NumberKeyboardLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }


    private void init(Context context) {
        View inflate = LayoutInflater.from(context).inflate(R.layout.keyboard_view, this, true);
        keyboardView = inflate.findViewById(R.id.view_keyboard);
        view = inflate.findViewById(R.id.view);
        view.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                hide();
            }
        });
    }


    public void hide() {
        this.setVisibility(GONE);
    }

    public void show(Context context) {
        //
        this.setVisibility(VISIBLE);


        InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);


// 强制隐藏软键盘
        imm.hideSoftInputFromWindow(view.getWindowToken(), 0);

    }

    //设置删除按钮的图标
    public void setBackKeyIcon(Drawable icon) {
        keyboardView.setBackKeyIcon(icon);
    }

    //打乱键盘的按键顺序,并重新绘制键盘
    public void shuffleKeyboard() {
        keyboardView.shuffleKeyboard();
    }

    //设置键盘的按键监听
    public void setIOnKeyboardListener(NumberKeyboardView.IOnKeyboardListener listener) {
        keyboardView.setIOnKeyboardListener(listener);
    }
}

 

6.使用

布局:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    >

  <EditText
      android:id="@+id/et"
      android:layout_width="match_parent"
      android:layout_height="wrap_content" />




  <Button
      android:onClick="bt"
      android:text="aaa"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content" />




</LinearLayout>

 

这里键盘的控件是直接通过new 一个NumberKeyboardLayout 对象,加在了Activity的布局中 

numberKeyboardLayout = new NumberKeyboardLayout(this);
        numberKeyboardLayout.setIOnKeyboardListener(this);

        addContentView(numberKeyboardLayout, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));

至此,数字小键盘就撸完了

最后,送上两个隐藏系统软键盘的工具类


    /**
     * Hide the soft input.
     *
     * @param activity The activity.
     */
    public static void hideSoftInput( Activity activity) {
        InputMethodManager imm =
                (InputMethodManager) activity.getSystemService(Activity.INPUT_METHOD_SERVICE);
        if (imm == null) return;
        View view = activity.getCurrentFocus();
        if (view == null) view = new View(activity);
        imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
    }

    /**
     * Hide the soft input.
     *
     * @param view The view.
     */
    public static void hideSoftInput(Context context, View view) {
        InputMethodManager imm =
                (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
        //noinspection ConstantConditions
        imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
    }

 

 

 gitHub:

https://github.com/wkangle

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值