通过自定义android键盘实现车牌号输入法

前言

很多的移动应用中经常需要限定用户输入特定的字符,比如吱妇保,微信钱包等在输入支付密码的时候就是直接调出的纯数字键盘并且不允许用户切换为非数字键盘,这在一定程度上方便了前端数据校验同时也有很好的用户体验.那么如果有比数字键盘更复杂的输入法我们该怎么实现呢?

车牌号输入法

最近我在项目中遇到的一个车牌输入法的问题,需要限定用户第一个字符只能是34个省份汉字简称,第二位为大写字母,余下5位为数字+字母大写的形势,整个界面如图:

Keyboard和KeyboardView类

android.inputmethodservice.Keyboard对象即代表了一个虚拟键盘,通过加载res/xml/下的.xml键盘布局文件来初始化一个虚拟键盘,其源码如下:

/**
 * Loads an XML description of a keyboard and stores the attributes of the keys. A keyboard
 * consists of rows of keys.
 * <p>The layout file for a keyboard contains XML that looks like the following snippet:</p>
 * <Keyboard
 *         android:keyWidth="%10p"
 *         android:keyHeight="50px"
 *         android:horizontalGap="2px"
 *         android:verticalGap="2px">
 *     <Row android:keyWidth="32px">
 *         <Key android:keyLabel="A">
 *         ...
 *     </Row>
 *     ...
 * </Keyboard>
 * @attr ref android.R.styleable#Keyboard_keyWidth
 * @attr ref android.R.styleable#Keyboard_keyHeight
 * @attr ref android.R.styleable#Keyboard_horizontalGap
 * @attr ref android.R.styleable#Keyboard_verticalGap
 */
public class Keyboard 
Keyboard属性

该段源码摘自SDK21 注释android:keyWidth=”%10p”可能有谬误,正确的应为android:keyWidth=”10%p”

android:keyWidth=”%10p” –>每个按键宽度
android:keyHeight=”50px” –>每个按键高度
android:horizontalGap=”2px” –>按键水平间隙
android:verticalGap=”2px” –>按键垂直间隙
Keyboard类下还有Keyboard.Row和Keyboard.Key两个内部类,分别代表了按键的一行和单个按键,<Keyboard>元素中的属性也都使用与<Row>和<Key>元素

汉字省份简称布局

res/xml/province_short_keyboard.xml

<?xml version="1.0" encoding="UTF-8"?>
<Keyboard
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:horizontalGap="4px"
    android:keyHeight="40dp"
    android:keyWidth="10.7%p"
    android:verticalGap="4px">
    <Row>
        <Key
            android:codes="0"
            android:keyEdgeFlags="left"
            android:keyLabel="京" />
        <Key
            android:codes="1"
            android:keyLabel="津" />
        <Key
            android:codes="2"
            android:keyLabel="冀" />
        <Key
            android:codes="3"
            android:keyLabel="鲁" />
        <Key
            android:codes="4"
            android:keyLabel="晋" />
        <Key
            android:codes="5"
            android:keyLabel="蒙" />
        <Key
            android:codes="6"
            android:keyLabel="辽" />
        <Key
            android:codes="7"
            android:keyLabel="吉" />
        <Key
            android:codes="8"
            android:keyEdgeFlags="right"
            android:keyLabel="黑" />
    </Row>
    <Row>
        <Key
            android:codes="9"
            android:keyEdgeFlags="left"
            android:keyLabel="沪" />
        <Key
            android:codes="10"
            android:keyLabel="苏" />
        <Key
            android:codes="11"
            android:keyLabel="浙" />
        <Key
            android:codes="12"
            android:keyLabel="皖" />
        <Key
            android:codes="13"
            android:keyLabel="闽" />
        <Key
            android:codes="14"
            android:keyLabel="赣" />
        <Key
            android:codes="15"
            android:keyLabel="豫" />
        <Key
            android:codes="16"
            android:keyLabel="鄂" />
        <Key
            android:codes="17"
            android:keyEdgeFlags="right"
            android:keyLabel="湘" />
    </Row>
    <Row>
        <Key
            android:codes="18"
            android:keyEdgeFlags="left"
            android:keyLabel="粤"
            />
        <Key
            android:codes="19"
            android:keyLabel="桂" />
        <Key
            android:codes="20"
            android:keyLabel="渝" />
        <Key
            android:codes="21"
            android:keyLabel="川" />
        <Key
            android:codes="22"
            android:keyLabel="贵" />
        <Key
            android:codes="23"
            android:keyLabel="云" />
        <Key
            android:codes="24"
            android:keyLabel="藏" />
        <Key
            android:codes="25"
            android:keyLabel="陕" />
        <Key
            android:codes="26"
            android:keyLabel="甘"
            android:keyEdgeFlags="right"
            />
    </Row>
    <Row android:rowEdgeFlags="bottom" android:keyWidth="14.28%p">
        <Key
            android:codes="27"
            android:keyLabel="青"
            android:keyEdgeFlags="left"
            />
        <Key
            android:codes="28"
            android:keyLabel="琼"
            />
        <Key
            android:codes="29"
            android:keyLabel="新"
            />
        <Key
            android:codes="30"
            android:keyLabel="港"
            />
        <Key
            android:codes="31"
            android:keyLabel="澳"
            />
        <Key
            android:codes="32"
            android:keyLabel="台"
            />
        <Key
            android:codes="33"
            android:keyLabel="宁"
            android:keyEdgeFlags="right"
            />
    </Row>
</Keyboard>
数字和大写字母键盘布局

res/xml/lettersanddigit_keyboard.xml

<?xml version="1.0" encoding="UTF-8"?>
<Keyboard
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:horizontalGap="4px"
    android:keyHeight="40dp"
    android:verticalGap="4px">
    <Row android:keyWidth="9.6%p">
        <Key
            android:codes="0"
            android:keyEdgeFlags="left"
            android:keyLabel="0" />
        <Key
            android:codes="1"
            android:keyLabel="1" />
        <Key
            android:codes="2"
            android:keyLabel="2" />
        <Key
            android:codes="3"
            android:keyLabel="3" />
        <Key
            android:codes="4"
            android:keyLabel="4" />
        <Key
            android:codes="5"
            android:keyLabel="5" />
        <Key
            android:codes="6"
            android:keyLabel="6" />
        <Key
            android:codes="7"
            android:keyLabel="7" />
        <Key
            android:codes="8"
            android:keyLabel="8" />
        <Key
            android:codes="9"
            android:keyEdgeFlags="right"
            android:keyLabel="9" />
    </Row>
    <Row android:keyWidth="9.6%p">
        <Key
            android:codes="10"
            android:keyEdgeFlags="left"
            android:keyLabel="Q" />
        <Key
            android:codes="11"
            android:keyLabel="W" />
        <Key
            android:codes="12"
            android:keyLabel="E" />
        <Key
            android:codes="13"
            android:keyLabel="R" />
        <Key
            android:codes="14"
            android:keyLabel="T" />
        <Key
            android:codes="15"
            android:keyLabel="Y" />
        <Key
            android:codes="16"
            android:keyLabel="U" />
        <Key
            android:codes="17"
            android:keyLabel="I" />
        <Key
            android:codes="18"
            android:keyLabel="O" />
        <Key
            android:codes="19"
            android:keyEdgeFlags="right"
            android:keyLabel="P" />
    </Row>
    <Row android:keyWidth="10.5%p">
        <Key
            android:codes="20"
            android:keyEdgeFlags="left"
            android:keyLabel="A"
            />
        <Key
            android:codes="21"
            android:keyLabel="S" />
        <Key
            android:codes="22"
            android:keyLabel="D" />
        <Key
            android:codes="23"
            android:keyLabel="F" />
        <Key
            android:codes="24"
            android:keyLabel="G" />
        <Key
            android:codes="25"
            android:keyLabel="H" />
        <Key
            android:codes="26"
            android:keyLabel="J" />
        <Key
            android:codes="27"
            android:keyLabel="K" />
        <Key
            android:codes="28"
            android:keyLabel="L"
            android:keyEdgeFlags="right"
            />
    </Row>
    <Row android:keyWidth="9.6%p"
        android:rowEdgeFlags="bottom">
        <Key
            android:codes="29"
            android:keyLabel="Z"
            android:keyEdgeFlags="left"
            />
        <Key
            android:codes="30"
            android:keyLabel="X"
            />
        <Key
            android:codes="31"
            android:keyLabel="C"
            />
        <Key
            android:codes="32"
            android:keyLabel="V"
            />
        <Key
            android:codes="33"
            android:keyLabel="B"
            />
        <Key
            android:codes="34"
            android:keyLabel="N"
            />
        <Key
            android:codes="35"
            android:keyLabel="M"
            />
        <Key
            android:codes="112"
            android:keyWidth="50sp"
            android:isRepeatable="true"
            android:keyLabel="&lt;-"

            />
        <Key
            android:codes="66"
            android:keyWidth="60sp"
            android:keyEdgeFlags="right"
            android:keyLabel="添加"
            />
    </Row>
</Keyboard>

android:keyEdgeFlags=”left” 表示该键位于本行最左第一个
android:rowEdgeFlags=”bottom”表示该行位于键盘最底部
android:codes=”66” 定义了该键的键值
android:keyLabel=”粤” 定义了该键显示的字符
此外还有android:popupCharacters=”¢£€¥????”属性,表示如果长按,就是弹出popup,请用户选择,选择的内容是popupCharacters中显示
注意: 如果该<key>有属性android:keyIcon=”@drawable/icon_add”则android:keyLabel属性无效,将以指定的icon来显示

定义好了键盘布局文件之后就需要来实现键盘的加载,按键监听等代码.
/src/me.kevingo.licensekeyboard.LicenseKeyboardUtil

package me.kevingo.licensekeyboard;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.inputmethodservice.Keyboard;
import android.inputmethodservice.KeyboardView;
import android.inputmethodservice.KeyboardView.OnKeyboardActionListener;
import android.os.Message;
import android.view.View;
import android.widget.EditText;
/**
 * Created by kevingo 2015/9/22.
 */
public class LicenseKeyboardUtil {
            
    private Context ctx;
    private KeyboardView keyboardView;
    private Keyboard k1;// 省份简称键盘
    private Keyboard k2;// 数字字母键盘
    private String provinceShort[];
    private String letterAndDigit[];
    private EditText edits[];
    private int currentEditText = 0;//默认当前光标在第一个EditText
    public LicenseKeyboardUtil(Context ctx, EditText edits[]) {
        this.ctx = ctx;
        this.edits = edits;
        k1 = new Keyboard(ctx, R.xml.province_short_keyboard);
        k2 = new Keyboard(ctx, R.xml.lettersanddigit_keyboard);
        keyboardView = (KeyboardView) ((Activity)ctx).findViewById(R.id.keyboard_view);
        keyboardView.setKeyboard(k1);
        keyboardView.setEnabled(true);
        //设置为true时,当按下一个按键时会有一个popup来显示<key>元素设置的android:popupCharacters=""
        keyboardView.setPreviewEnabled(true);
        //设置键盘按键监听器
        keyboardView.setOnKeyboardActionListener(listener);
        provinceShort = new String[]{
           "京", "津", "冀", "鲁", "晋", "蒙", "辽", "吉", "黑"
                , "沪", "苏", "浙", "皖", "闽", "赣", "豫", "鄂", "湘"
                , "粤", "桂", "渝", "川", "贵", "云", "藏", "陕", "甘"
                , "青", "琼", "新", "港", "澳", "台", "宁"};

        letterAndDigit = new String[]{
           "0","1", "2", "3", "4", "5", "6", "7", "8", "9"
                , "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P"
                , "A", "S", "D", "F", "G", "H", "J", "K", "L"
                , "Z", "X", "C", "V", "B", "N", "M"};
    }

    private OnKeyboardActionListener listener = new OnKeyboardActionListener() {
        @Override
        public void swipeUp() {
        }

        @Override
        public void swipeRight() {
        }
        @Override
        public void swipeLeft() {
        }
        @Override
        public void swipeDown() {
        }
        @Override
        public void onText(CharSequence text) {
        }
        @Override
        public void onRelease(int primaryCode) {
        }

        @Override
        public void onPress(int primaryCode) {
        }
        @Override
        public void onKey(int primaryCode, int[] keyCodes) {
            if(primaryCode == 112){ //xml中定义的删除键值为112
                edits[currentEditText].setText("");//将当前EditText置为""并currentEditText-1
                currentEditText--;
                if(currentEditText < 1){
                    //切换为省份简称键盘
                    keyboardView.setKeyboard(k1);
                }
                if(currentEditText < 0){
                    currentEditText = 0;
                }
            }else if(primaryCode == 66){ //xml中定义的完成键值为66
                Intent intent = new Intent();
                String license = "";
                for (int i=0;i<7;i++){
                    license += edits[i].getText().toString();
                }
                intent.putExtra(MainActivity.INPUT_LICENSE_KEY, license);
                intent.setAction(MainActivity.INPUT_LICENSE_COMPLETE);
                ctx.sendBroadcast(intent);
            }else { //其它字符按键
                if (currentEditText == 0) {
                    //如果currentEditText==0代表当前为省份键盘,
                    // 按下一个按键后,设置相应的EditText的值
                    // 然后切换为字母数字键盘
                    //currentEditText+1
                    edits[0].setText(provinceShort[primaryCode]);
                    currentEditText = 1;
                    //切换为字母数字键盘
                    keyboardView.setKeyboard(k2);
                }else{
                    //第二位必须大写字母
                    if(currentEditText == 1 && !letterAndDigit[primaryCode].matches("[A-Z]{1}")){
                        return ;
                    }
                    edits[currentEditText].setText(letterAndDigit[primaryCode]);
                    currentEditText++;
                    if (currentEditText > 6) {
                        currentEditText = 6;
                    }
                }
            }
        }
    };

    /**
     * 显示键盘
     */
    public void showKeyboard() {
        int visibility = keyboardView.getVisibility();
        if (visibility == View.GONE || visibility == View.INVISIBLE) {
            keyboardView.setVisibility(View.VISIBLE);
        }
    }
    /**
     * 隐藏键盘
     */
    public void hideKeyboard() {
        int visibility = keyboardView.getVisibility();
        if (visibility == View.VISIBLE) {
            keyboardView.setVisibility(View.INVISIBLE);
        }
    }
}

所有的按键操作可参考代码注释 ,主界面布局文件如下
lres/ayout/activity_main.xml

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

    <Button
        android:id="@+id/btn_add_car"
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:text="输入车牌"
        android:layout_marginLeft="10dp"
        android:layout_marginRight="10dp"
        android:layout_centerVertical="true"
        android:textColor="#FFFFFF"
        android:background="#15A0F5"/>

        <LinearLayout
            android:id="@+id/ll_car_license_inputbox_content"
            android:layout_width="320dp"
            android:layout_height="wrap_content"
            android:layout_centerHorizontal="true"
            android:background="@android:color/white"
            android:orientation="vertical"
            android:padding="20dp"
            android:layout_centerVertical="true"
            android:visibility="gone"
            >
            <TextView
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:text="输入车辆信息"
                android:textSize="26sp"
                android:gravity="center_horizontal"
                android:paddingBottom="15dp"
                android:textColor="#9DA9B4"
                />
            <LinearLayout
                android:id="@+id/ll_license_input_boxes_content"
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:orientation="horizontal"
                android:gravity="center_horizontal">
                <EditText
                    android:id="@+id/et_car_license_inputbox1"
                    android:background="@drawable/car_license_inputbox"
                    android:gravity="center_horizontal|center_vertical"
                    android:layout_width="40dp"
                    android:layout_height="40dp" />
                <EditText
                    android:id="@+id/et_car_license_inputbox2"
                    android:background="@drawable/car_license_inputbox"
                    android:gravity="center_horizontal|center_vertical"
                    android:layout_width="40dp"
                    android:layout_height="40dp" />
                <EditText
                    android:id="@+id/et_car_license_inputbox3"
                    android:background="@drawable/car_license_inputbox"
                    android:gravity="center_horizontal|center_vertical"
                    android:layout_width="40dp"
                    android:layout_height="40dp" />
                <EditText
                    android:id="@+id/et_car_license_inputbox4"
                    android:background="@drawable/car_license_inputbox"
                    android:gravity="center_horizontal|center_vertical"
                    android:layout_width="40dp"
                    android:layout_height="40dp" />
                <EditText
                    android:id="@+id/et_car_license_inputbox5"
                    android:background="@drawable/car_license_inputbox"
                    android:gravity="center_horizontal|center_vertical"
                    android:layout_width="40dp"
                    android:layout_height="40dp" />
                <EditText
                    android:id="@+id/et_car_license_inputbox6"
                    android:background="@drawable/car_license_inputbox"
                    android:gravity="center_horizontal|center_vertical"
                    android:layout_width="40dp"
                    android:layout_height="40dp" />
                <EditText
                    android:id="@+id/et_car_license_inputbox7"
                    android:background="@drawable/car_license_inputbox"
                    android:gravity="center_horizontal|center_vertical"
                    android:layout_width="40dp"
                    android:layout_height="40dp" />
            </LinearLayout>

            <LinearLayout
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="30dp"
                android:orientation="horizontal"
                android:gravity="center_horizontal">
                <Button
                    android:layout_width="fill_parent"
                    android:layout_height="wrap_content"
                    android:textSize="22sp"
                    android:paddingTop="10dp"
                    android:paddingBottom="10dp"
                    android:paddingRight="15dp"
                    android:layout_weight="1"
                    android:paddingLeft="15dp"
                    android:layout_marginRight="15dp"
                    android:text="小轿车"
                    android:layout_alignParentLeft="true"
                    android:background="#0068B7"
                    android:textColor="@android:color/white"
                    android:id="@+id/btn_addcar_type_1" />
                <!--车辆类型:1、小车 2、越野-->
                <Button
                    android:layout_width="fill_parent"
                    android:layout_height="wrap_content"
                    android:textSize="22sp"
                    android:paddingTop="10dp"
                    android:paddingBottom="10dp"
                    android:paddingRight="15dp"
                    android:paddingLeft="15dp"
                    android:text="越野车"
                    android:layout_weight="1"
                    android:layout_marginLeft="15dp"
                    android:id="@+id/btn_addcar_type_2"
                    android:layout_alignParentRight="true"
                    android:background="@drawable/btn_transparent_gray_border"
                    android:textColor="#9DA9B4"
                    />
            </LinearLayout>
        </LinearLayout>

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:orientation="vertical"
        >
        <android.inputmethodservice.KeyboardView
            android:id="@+id/keyboard_view"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:focusable="true"
            android:paddingTop="5dp"
            android:layout_gravity="center_horizontal"
            android:focusableInTouchMode="true"
            android:background="#F0EFF4"
            android:keyBackground="@android:color/white"
            android:keyTextColor="#5E5E5E"
            android:shadowColor="#ffffff"
            android:shadowRadius="0.0"
            android:keyTextSize="18sp"
            android:labelTextSize="18sp"
            android:visibility="gone" />
    </LinearLayout>
</RelativeLayout>

Activity如下:
src/me.kevingo.licensekeyboard.MainActivity

package me.kevingo.licensekeyboard;

import android.app.AlertDialog;
import android.app.DownloadManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity implements View.OnClickListener{
            

    public static final String INPUT_LICENSE_COMPLETE = "me.kevingo.licensekeyboard.input.comp";
    public static final String INPUT_LICENSE_KEY = "LICENSE";

    private EditText inputbox1,inputbox2,
            inputbox3,inputbox4,
            inputbox5,inputbox6,inputbox7;
    private LinearLayout boxesContainer;
    private LicenseKeyboardUtil keyboardUtil;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        inputbox1 = (EditText) this.findViewById(R.id.et_car_license_inputbox1);
        inputbox2 = (EditText) this.findViewById(R.id.et_car_license_inputbox2);
        inputbox3 = (EditText) this.findViewById(R.id.et_car_license_inputbox3);
        inputbox4 = (EditText) this.findViewById(R.id.et_car_license_inputbox4);
        inputbox5 = (EditText) this.findViewById(R.id.et_car_license_inputbox5);
        inputbox6 = (EditText) this.findViewById(R.id.et_car_license_inputbox6);
        inputbox7 = (EditText) this.findViewById(R.id.et_car_license_inputbox7);
        Button button = (Button) this.findViewById(R.id.btn_add_car);
        button.setOnClickListener(this);
        boxesContainer = (LinearLayout) this.findViewById(R.id.ll_car_license_inputbox_content);

        //输入车牌完成后的intent过滤器
        IntentFilter finishFilter = new IntentFilter(INPUT_LICENSE_COMPLETE);

        final BroadcastReceiver receiver =  new  BroadcastReceiver() {
            @Override
            public   void  onReceive(Context context, Intent intent) {
                String license = intent.getStringExtra(INPUT_LICENSE_KEY);
                if(license != null && license.length() > 0){
                    boxesContainer.setVisibility(View.GONE);
                    if(keyboardUtil != null){
                        keyboardUtil.hideKeyboard();
                    }

                    AlertDialog alertDialog;
                    AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
                    builder.setMessage("车牌号为:" + license);
                    alertDialog = builder.create();
                    alertDialog.setCancelable(true);
                    alertDialog.show();
                }
                MainActivity.this.unregisterReceiver(this);
            }
        };
        this.registerReceiver(receiver, finishFilter);
    }

    @Override
    public void onClick(View v) {
        switch(v.getId()){
            case R.id.btn_add_car:
                boxesContainer.setVisibility(View.VISIBLE);
                keyboardUtil = new LicenseKeyboardUtil(this,new EditText[]{inputbox1,inputbox2,inputbox3,
                        inputbox4,inputbox5,inputbox6,inputbox7});
                keyboardUtil.showKeyboard();
            break;
        }
    }
}

相关源码下载地址

android自定义车牌号输入法.rar

© 版权声明
THE END
如果内容对您有所帮助,就支持一下吧!
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容