android自定义键盘

其实对于简单的键盘,比如数字键盘这种,使用自定义view即可,这里为了学习android的键盘知识,继承android系统键盘。
先看效果图:
在这里插入图片描述
在这里插入图片描述

一、在res/xml目录下新建一个键盘布局文件

<?xml version="1.0" encoding="utf-8"?>
<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
    android:keyWidth="22.5%p"
    android:keyHeight="8%p"
    android:horizontalGap="1%p"
    android:verticalGap="0.5%p"
    >
    <Row>
        <Key android:codes="1" android:keyLabel="1" android:isRepeatable="true"/>
        <Key android:codes="2" android:keyLabel="2" android:isRepeatable="true"/>
        <Key android:codes="3" android:keyLabel="3" android:isRepeatable="true"/>
        <Key android:codes="-5" android:keyIcon="@drawable/ic_delete_key" android:isRepeatable="true"/>
    </Row>
    <Row>
        <Key android:codes="4" android:keyLabel="4" android:isRepeatable="true"/>
        <Key android:codes="5" android:keyLabel="5" android:isRepeatable="true"/>
        <Key android:codes="6" android:keyLabel="6" android:isRepeatable="true"/>
        <Key android:codes="-2" android:keyHeight="25%p"/>
    </Row>
    <Row>
        <Key android:codes="7" android:keyLabel="7" android:isRepeatable="true"/>
        <Key android:codes="8" android:keyLabel="8" android:isRepeatable="true"/>
        <Key android:codes="9" android:keyLabel="9" android:isRepeatable="true"/>
    </Row>
    <Row>
        <Key android:codes="0" android:keyLabel="0" android:keyWidth="46%p" android:isRepeatable="true"/>
        <Key android:codes="-3" android:keyLabel="."/>
    </Row>

</Keyboard>

keyWidth和keyHeight分别代表按键的宽高,可以用百分比或者数值的方式。
horizontalGap和verticalGap分别代表横向和竖向的按键间隔。
codes是按键编号,可自定义。
keyLabel在按键上显示的字体。
isRepeatable长按重复点击。
keyIcon按钮图标,但是好像不能自动显示出来,需要在自定义的KeyboardView中去绘制。

二、继承KeyboardView

public class PayKeyboardView extends KeyboardView {

    private Paint payPaint;
    private boolean enablePay; // 是否允许付款

    public PayKeyboardView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

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

    private void init() {
        payPaint = new Paint();
        payPaint.setTextAlign(Paint.Align.CENTER);
        payPaint.setTextSize(ScreenUtils.sp2px(getContext(), 24));
        payPaint.setColor(Color.WHITE);
        payPaint.setAntiAlias(true);
    }

    @Override
    public void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        Keyboard keyboard = getKeyboard();
        if (keyboard == null) return;
        List<Keyboard.Key> keys = keyboard.getKeys();
        if (keys != null && keys.size() > 0) {
            for (Keyboard.Key key : keys) {
                if (key.codes[0] == -5) {
                    // 删除
                    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);
                    }
                } else if (key.codes[0] == -2) {
                    // 付款
                    Drawable dr;
                    if (enablePay) {
                        dr = getContext().getResources().getDrawable(R.drawable.bg_pay);
                    } else {
                        dr = getContext().getResources().getDrawable(R.drawable.bg_pay_n);
                    }
                    dr.setBounds(key.x, key.y, key.x + key.width, key.y + key.height);
                    dr.draw(canvas); // 绘制付款背景

                    // 竖向绘制付款文字
                    String text = "付款";
                    Rect rect = new Rect(key.x, key.y, key.x + key.width, key.y + key.height);
                    Paint.FontMetricsInt fontMetrics = payPaint.getFontMetricsInt();
                    int baseline = (rect.bottom + rect.top) / 2 - fontMetrics.bottom;
                    payPaint.setTextAlign(Paint.Align.CENTER);
                    for (int i=0;i<text.length();i++) {
                        canvas.drawText(String.valueOf(text.charAt(i)), rect.centerX(), baseline, payPaint);
                        baseline += (fontMetrics.bottom - fontMetrics.top);
                    }
                }

            }
        }
    }

    /**
     * 允许付款
     * @param pay
     */
    public void enablePay(boolean pay) {
        if (enablePay == pay) {
            return;
        }
        enablePay = pay;
        invalidate();
    }

    /**
     * 是否允许付款
     */
    public boolean getEnablePay() {
        return enablePay;
    }
}

该类主要实现键盘按钮的绘制,包括一些个性化的实现。

三、在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">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/white"
        android:gravity="center_horizontal"
        android:orientation="vertical">

        <com.showfitness.commonlibrary.widget.TitleBarView
            android:id="@+id/title_bar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:title_name="向商家付款" />

        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="@dimen/dp_32"
            android:src="@drawable/ic_shop"/>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:paddingTop="@dimen/dp_10"
            android:paddingBottom="@dimen/dp_10"
            android:layout_marginLeft="@dimen/dp_30"
            android:layout_marginRight="@dimen/dp_30"
            android:layout_marginTop="@dimen/dp_42">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="¥"
                android:textColor="#000000"
                android:textSize="24sp"/>

            <EditText
                android:id="@+id/et_money"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginLeft="@dimen/dp_20"
                android:background="@null"
                android:textSize="24sp"
                android:focusable="true"
                android:focusableInTouchMode="true"
                android:textStyle="bold"
                android:textColor="@color/common_black" />

        </LinearLayout>

        <View
            android:layout_width="match_parent"
            android:layout_height="1px"
            android:layout_marginLeft="@dimen/dp_20"
            android:layout_marginRight="@dimen/dp_20"
            android:background="#EEEEEE"/>

    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#EBEBEB"
        android:layout_alignParentBottom="true"
        android:gravity="center_horizontal">

        <com.showfitness.commonlibrary.widget.keyboard.PayKeyboardView
            android:id="@+id/keyboard"
            android:layout_width="match_parent"
            android:layout_height="@dimen/dp_300"
            android:layout_marginTop="@dimen/dp_5"
            android:layout_marginBottom="@dimen/dp_5"
            android:background="#EBEBEB"
            android:keyBackground="@drawable/bg_keyboard_selector"
            android:keyTextColor="@color/keyboard_text"
            android:keyTextSize="28sp"
            android:shadowRadius="0.0" />
    </LinearLayout>
</RelativeLayout>

由于我们继承的系统的KeyboardView,在布局中可以使用一些系统定义的属性。
keyBackground -> 按钮的背景,可以设置按压状态
keyTextColor -> 键盘文字颜色
keyTextSize -> 键盘文字大小
shadowRadius=“0.0” -> 设置这行,不然文字会模糊
等等

四、自定义键盘辅助类

public class CustomerKeyboardUtil {

    private Context mContext;
    private PayKeyboardView mKeyboardView;
    private EditText mEditText;
    private Listener listener;

    @SuppressLint("ClickableViewAccessibility")
    public CustomerKeyboardUtil(Context context, PayKeyboardView keyboardView, EditText editText) {
        this.mContext = context;
        this.mKeyboardView = keyboardView;
        this.mEditText = editText;
        initKeyboard();
        mEditText.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                // 文本框点击事件,隐藏系统键盘,显示自定义键盘
                if (event.getAction() == MotionEvent.ACTION_UP) {
                    hideSystemKeyboard((EditText) v);
                    mKeyboardView.setVisibility(View.VISIBLE);
                }
                return false;
            }
        });
        mEditText.setOnFocusChangeListener(new View.OnFocusChangeListener() {
            @Override
            public void onFocusChange(View v, boolean hasFocus) {
                // 当失去焦点时,隐藏键盘,但是没啥用
                if (v instanceof EditText) {
                    if (!hasFocus) {
                        mKeyboardView.setVisibility(View.GONE);
                    } else {
                        hideSystemKeyboard((EditText) v);
                        mKeyboardView.setVisibility(View.VISIBLE);
                    }
                }

            }
        });
        // 自动获取焦点
        mEditText.requestFocus();
    }

    /**
     * 隐藏系统键盘
     * @param v
     */
    private void hideSystemKeyboard(EditText v) {
        this.mEditText = v;
        InputMethodManager imm = (InputMethodManager) mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
        if(imm == null){
            return;
        }
        boolean isOpen = imm.isActive();
        if (isOpen) {
            imm.hideSoftInputFromWindow(v.getWindowToken(),0);
        }
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){
            v.setShowSoftInputOnFocus(false);
        }else{
            v.setInputType(0);
        }
    }

    /**
     * 初始化自定义键盘
     */
    private void initKeyboard() {
        Keyboard keyboard = new Keyboard(mContext, R.xml.customer_keyboard); // 绑定自定义键盘视图
        mKeyboardView.setKeyboard(keyboard);
        mKeyboardView.setEnabled(true);
        mKeyboardView.setOnKeyboardActionListener(actionListener);
    }

    private KeyboardView.OnKeyboardActionListener actionListener = new KeyboardView.OnKeyboardActionListener() {
        @Override
        public void onPress(int primaryCode) {
            // 禁止预览
            mKeyboardView.setPreviewEnabled(false);
        }

        @Override
        public void onRelease(int primaryCode) {

        }

        @Override
        public void onKey(int primaryCode, int[] keyCodes) {
            Editable editable = mEditText.getText();
            int start = mEditText.getSelectionStart();
            int end = mEditText.getSelectionEnd();
            if(primaryCode == Keyboard.KEYCODE_DELETE){
                // 删除
                if(editable != null && editable.length() > 0){
                    if(start == end){
                        editable.delete(start -1, start);
                    }else{
                        editable.delete(start,end);
                    }
                }
            } else if(primaryCode == -3){
                // 小数点
                if (TextUtils.isEmpty(editable) || editable.toString().contains(".")) {
                    // 小数点不能是第一个,只能有一个小数点
                    return;
                }
                mEditText.append(".");
            } else if(primaryCode == -2){
                // 付款
                if (mKeyboardView.getEnablePay()) {
                    if (listener != null) {
                        listener.onPay(getText());
                    }
                }
            } else if(primaryCode == 0){
                // 按键0
                if (!TextUtils.isEmpty(editable) && editable.toString().startsWith("0")) {
                    // 0不能前面连续两个
                    return;
                }
                mEditText.append(String.valueOf(primaryCode));
            } else {
                // 其他数字
                if (!TextUtils.isEmpty(editable) && editable.toString().startsWith("0")) {
                    // 第一个为零,后一位只能是小数点
                    return;
                }
                mEditText.append(String.valueOf(primaryCode));
            }
            mKeyboardView.enablePay(!TextUtils.isEmpty(mEditText.getText()));
            if (listener != null) {
                listener.onChange(getText());
            }
        }

        @Override
        public void onText(CharSequence text) {
        }

        @Override
        public void swipeLeft() {

        }

        @Override
        public void swipeRight() {

        }

        @Override
        public void swipeDown() {

        }

        @Override
        public void swipeUp() {

        }
    };

    /**
     * 格式化结果
     * @return
     */
    private String getText() {
        String text = mEditText.getText().toString();
        if (TextUtils.isEmpty(mEditText.getText())) {
            return text;
        }
        if (text.endsWith(".")) {
            // 去掉最后一位是小数点
            text.replace(".", "");
        }
        return text;
    }

    public Listener getListener() {
        return listener;
    }

    /**
     * 是否点击键盘
     * @return
     */
    public boolean isClickKeyboardView(int x, int y) {
        Rect rect = new Rect();
        mKeyboardView.getGlobalVisibleRect(rect);
        return rect.contains(x, y);
    }

    /**
     * 隐藏软键盘
     */
    public void hideKeyboard() {
        mKeyboardView.setVisibility(View.GONE);
    }


    public void setListener(Listener listener) {
        this.listener = listener;
    }

    /**
     * 支付监听
     */
    public interface Listener {
        void onChange(CharSequence text);
        // 支付
        void onPay(CharSequence text);
    }
}

将键盘和文本绑定在一起,监听键盘事件。

五、在activity中使用

public class PayStoreActivity extends BaseActivity {

    private CustomerKeyboardUtil customerKeyboardUtil;

    @BindView(R2.id.et_money)
    EditText etMoney;
    @BindView(R2.id.keyboard)
    PayKeyboardView keyboardView;

    @Override
    protected int getLayoutId() {
        return R.layout.activity_pay_store;
    }

    @Override
    public void onBindView(Bundle savedInstanceState) {
        // 绑定键盘辅助类
        customerKeyboardUtil = new CustomerKeyboardUtil(mContext, keyboardView, etMoney);
        customerKeyboardUtil.setListener(new CustomerKeyboardUtil.Listener() {
            @Override
            public void onChange(CharSequence text) {
            }

            @Override
            public void onPay(CharSequence text) {
                // 去付款
            }
        });
    }

    /**
     * 当点击空白区域时,收起键盘
     * @param me
     * @return
     */
    @Override
    public boolean dispatchTouchEvent(MotionEvent me) {
        if (me.getAction() == MotionEvent.ACTION_DOWN) {  //把操作放在用户点击的时候
            View v = getCurrentFocus();      //得到当前页面的焦点,ps:有输入框的页面焦点一般会被输入框占据
            if (isShouldHideKeyboard(v, me)) { //判断用户点击的是否是输入框以外的区域
                customerKeyboardUtil.hideKeyboard();   //收起键盘
            }
        }
        return super.dispatchTouchEvent(me);
    }

    /**
     * 根据EditText所在坐标和用户点击的坐标相对比,来判断是否隐藏键盘,因为当用户点击EditText时则不能隐藏
     *
     * @param v
     * @param event
     * @return
     */
    private boolean isShouldHideKeyboard(View v, MotionEvent event) {
        if (v != null && (v instanceof EditText)) {  //判断得到的焦点控件是否包含EditText
            int[] location = {0, 0};
            v.getLocationInWindow(location);
            int left = location[0],    //得到输入框在屏幕中上下左右的位置
                    top = location[1],
                    bottom = top + v.getHeight(),
                    right = left + v.getWidth();
            if ((event.getX() > left && event.getX() < right
                    && event.getY() > top && event.getY() < bottom) ||
                    (customerKeyboardUtil.isClickKeyboardView((int)event.getX(), (int)event.getY()))) {
                // 点击位置如果是EditText的区域或者键盘区域,忽略它,不收起键盘。
                return false;
            } else {
                return true;
            }
        }
        // 如果焦点不是EditText则忽略
        return false;
    }
}

这里处理了点击空白处收起键盘的操作。

发布了216 篇原创文章 · 获赞 91 · 访问量 25万+

猜你喜欢

转载自blog.csdn.net/yu75567218/article/details/103305838