最近在android项目中,遇到需要android车牌键盘的需求(需要支持普通车牌,新能源,警车,军车,领事馆车,教练车以及特种车辆等车牌

一、示例图

话不多说,分享一下android车牌键盘效果图,以及源码

1、省份选择,也可以更多里选择其他特种车辆例如数字开头的,或者“使”,“民”等

2、号码填写,过滤掉了字母O,I等不存在的号

3、可选择警、学、挂等特殊车辆后缀

二、核心代码

1、键盘控制器

package com.parkingwang.keyboard;import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.Toast;import com.parkingwang.keyboard.engine.KeyboardEntry;
import com.parkingwang.keyboard.engine.NumberType;
import com.parkingwang.keyboard.view.InputView;
import com.parkingwang.keyboard.view.KeyboardView;
import com.parkingwang.keyboard.view.OnKeyboardChangedListener;
import com.parkingwang.vehiclekeyboard.R;import java.util.LinkedHashSet;
import java.util.Set;/***/
public class KeyboardInputController {private static final String TAG = "KeyboardInputController";private final KeyboardView mKeyboardView;private final InputView mInputView;private final Set<OnInputChangedListener> mOnInputChangedListeners = new LinkedHashSet<>(4);private boolean mLockedOnNewEnergyType = false;private boolean mDebugEnabled = true;private boolean mSwitchVerify = true;private MessageHandler mMessageHandler;/*** 使用键盘View和输入View,创建键盘输入控制器** @param keyboardView 键盘View* @param inputView    输入框View*/public KeyboardInputController(KeyboardView keyboardView, InputView inputView) {mKeyboardView = keyboardView;mInputView = inputView;// 绑定输入框被选中的触发事件:更新键盘mInputView.addOnFieldViewSelectedListener(new InputView.OnFieldViewSelectedListener() {@Overridepublic void onSelectedAt(int index) {final String number = mInputView.getNumber();if (mDebugEnabled) {Log.w(TAG, "点击输入框更新键盘, 号码:" + number + ",序号:" + index);}// 除非锁定新能源类型,否则都让引擎自己检测车牌类型if (mLockedOnNewEnergyType) {mKeyboardView.update(number, index, false, NumberType.NEW_ENERGY);} else {mKeyboardView.update(number, index, false, NumberType.AUTO_DETECT);}}});// 绑定键盘按键点击事件:更新输入框字符操作,输入框长度变化mKeyboardView.addKeyboardChangedListener(syncKeyboardInputState());// 检测键盘更新,尝试自动提交只有一位文本按键的操作
//        mKeyboardView.addKeyboardChangedListener(new AutoCommit(mInputView));// 触发键盘更新回调mKeyboardView.addKeyboardChangedListener(triggerInputChangedCallback());}/*** 使用键盘View和输入View,创建键盘输入控制器** @param keyboardView 键盘View* @param inputView    输入框View* @return KeyboardInputController*/public static KeyboardInputController with(KeyboardView keyboardView, InputView inputView) {return new KeyboardInputController(keyboardView, inputView);}/*** 绑定新能源车牌类型锁定按钮实现接口。* 当键盘切换新能源车牌时,会调用此接口相关函数来更改锁定按钮状态。** @param proxy 锁定按钮代理实现接口* @return KeyboardInputController*/public KeyboardInputController bindLockTypeProxy(final LockNewEnergyProxy proxy) {// 点击按钮时,切换新能源车牌绑定状态proxy.setOnClickListener(new RadioGroup.OnCheckedChangeListener() {@Overridepublic void onCheckedChanged(RadioGroup group, int checkedId) {tryLockNewEnergyType(!mLockedOnNewEnergyType);}});
//        proxy.setOnClickListener(new View.OnClickListener() {
//            @Override
//            public void onClick(View v) {
//                tryLockNewEnergyType(!mLockedOnNewEnergyType);
//            }
//        });// 新能源车牌绑定状态,同步键盘更新的新能源类型mKeyboardView.addKeyboardChangedListener(new OnKeyboardChangedListener.Simple() {@Overridepublic void onKeyboardChanged(KeyboardEntry keyboard) {// 如果键盘更新当前为新能源类型时,强制锁定为新能源类型if (NumberType.NEW_ENERGY.equals(keyboard.currentNumberType)) {tryLockNewEnergyType(true);}// 同步锁定按钮proxy.onNumberTypeChanged(NumberType.NEW_ENERGY.equals(keyboard.currentNumberType));}});return this;}/*** 使用默认Toast的消息显示处理接口。* 默认时,键盘状态切换的提示消息,通过Toast接口来显示。** @return KeyboardBinder*/public KeyboardInputController useDefaultMessageHandler() {return setMessageHandler(new MessageHandler() {@Overridepublic void onMessageError(int message) {Toast.makeText(mKeyboardView.getContext(), message, Toast.LENGTH_SHORT).show();}@Overridepublic void onMessageTip(int message) {Toast.makeText(mKeyboardView.getContext(), message, Toast.LENGTH_SHORT).show();}});}/*** 更新输入组件的车牌号码,并默认选中最后编辑位。** @param number 车牌号码*/public void updateNumber(String number) {updateNumberLockType(number, false);}/*** 更新输入组件的车牌号码,指定是否锁定新能源类型,并默认选中最后编辑位。** @param number                车牌号码* @param lockedOnNewEnergyType 是否锁定为新能源类型*/public void updateNumberLockType(String number, boolean lockedOnNewEnergyType) {final String newNumber = number == null ? "" : number;mLockedOnNewEnergyType = lockedOnNewEnergyType;mInputView.updateNumber(newNumber);mInputView.performLastPendingFieldView();}/*** 设置键盘提示消息回调接口** @param handler 消息回调接口* @return KeyboardBinder*/public KeyboardInputController setMessageHandler(MessageHandler handler) {mMessageHandler = Objects.notNull(handler);return this;}/*** 添加输入变更回调接口** @param listener 回调接口* @return KeyboardInputController*/public KeyboardInputController addOnInputChangedListener(OnInputChangedListener listener) {mOnInputChangedListeners.add(Objects.notNull(listener));return this;}/*** 移除输入变更回调接口** @param listener 回调接口* @return KeyboardInputController*/public KeyboardInputController removeOnInputChangedListener(OnInputChangedListener listener) {mOnInputChangedListeners.remove(Objects.notNull(listener));return this;}/*** 设置是否在新能源和普通车牌切换的时候校验规则* @param verify 是否校验* @return KeyboardInputController*/public KeyboardInputController setSwitchVerify(boolean verify){mSwitchVerify = verify;return this;}/*** 设置是否启用调试信息** @param enabled 是否启用* @return KeyboardInputController*/public KeyboardInputController setDebugEnabled(boolean enabled) {mDebugEnabled = enabled;return this;}//private void updateInputViewItemsByNumberType(NumberType type) {// 如果检测到的车牌号码为新能源、地方武警,需要显示第8位车牌final boolean show;if (NumberType.NEW_ENERGY.equals(type) || NumberType.WJ2012.equals(type) || mLockedOnNewEnergyType) {show = true;} else {show = false;}mInputView.set8thVisibility(show);}private void tryLockNewEnergyType(boolean toLock) {// not changedif (toLock == mLockedOnNewEnergyType) {return;}final boolean completed = mInputView.isCompleted();if (toLock) {triggerLockEnergyType(completed);} else {// unlocktriggerUnlockEnergy(completed);}}// 解锁新能源车牌private void triggerUnlockEnergy(boolean completed) {mLockedOnNewEnergyType = false;
//        mMessageHandler.onMessageTip(R.string.pwk_now_is_normal);final boolean lastItemSelected = mInputView.isLastFieldViewSelected();updateInputViewItemsByNumberType(NumberType.AUTO_DETECT);if (completed || lastItemSelected) {mInputView.performLastPendingFieldView();} else {mInputView.rePerformCurrentFieldView();}}// 锁定新能源车牌private void triggerLockEnergyType(boolean completed) {if (!mSwitchVerify || Texts.isNewEnergyType(mInputView.getNumber())) {mLockedOnNewEnergyType = true;
//            mMessageHandler.onMessageTip(R.string.pwk_now_is_energy);updateInputViewItemsByNumberType(NumberType.NEW_ENERGY);if (completed) {mInputView.performNextFieldView();} else {mInputView.rePerformCurrentFieldView();}} else {mMessageHandler.onMessageError(R.string.pwk_change_to_energy_disallow);}}// 输入变更回调private OnKeyboardChangedListener triggerInputChangedCallback() {return new OnKeyboardChangedListener.Simple() {@Overridepublic void onTextKey(String text) {notifyChanged();}@Overridepublic void onDeleteKey() {notifyChanged();}@Overridepublic void onConfirmKey() {final String number = mInputView.getNumber();for (OnInputChangedListener listener : mOnInputChangedListeners) {listener.onCompleted(number, false);}}private void notifyChanged() {final boolean completed = mInputView.isCompleted();final String number = mInputView.getNumber();try {for (OnInputChangedListener listener : mOnInputChangedListeners) {listener.onChanged(number, completed);}} finally {if (completed) {for (OnInputChangedListener listener : mOnInputChangedListeners) {listener.onCompleted(number, true);}}}}};}private OnKeyboardChangedListener syncKeyboardInputState() {return new OnKeyboardChangedListener.Simple() {@Overridepublic void onTextKey(String text) {mInputView.updateSelectedCharAndSelectNext(text);}@Overridepublic void onDeleteKey() {mInputView.removeLastCharOfNumber();}@Overridepublic void onKeyboardChanged(KeyboardEntry keyboard) {if (mDebugEnabled) {Log.w(TAG, "键盘已更新," +"预设号码号码:" + keyboard.presetNumber +",最终探测类型:" + keyboard.currentNumberType);}updateInputViewItemsByNumberType(keyboard.currentNumberType);}};}/*** 锁定车牌类型代理接口*/public interface LockNewEnergyProxy {/*** 设置点击切换车牌类型的点击回调接口。通常使用Button来实现。** @param listener 点击回调接口。*/void setOnClickListener(RadioGroup.OnCheckedChangeListener listener);/*** 当车牌类型发生变化时,此方法被回调。** @param isNewEnergyType 当前是否为新能源类型*/void onNumberTypeChanged(boolean isNewEnergyType);}@Deprecatedpublic interface LockTypeProxy extends LockNewEnergyProxy {}/*** 使用Button组件实现的锁定新能源车牌切换逻辑*/public static class ButtonProxyImpl implements LockNewEnergyProxy {private final RadioGroup mButton;public ButtonProxyImpl(RadioGroup button) {mButton = button;}@Overridepublic void setOnClickListener(RadioGroup.OnCheckedChangeListener listener) {mButton.setOnCheckedChangeListener(listener);}@Overridepublic void onNumberTypeChanged(boolean isNewEnergyType) {if (isNewEnergyType) {
//                mButton.setText(R.string.pwk_change_to_normal);} else {
//                mButton.setText(R.string.pwk_change_to_energy);}}}@Deprecatedpublic static class ButtonProxy extends ButtonProxyImpl {public ButtonProxy(RadioGroup button) {super(button);}}
}

2、键盘布局xml文件

<?xml version="1.0" encoding="utf-8"?>
<merge style="@style/PWKInputViewStyle"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"android:orientation="horizontal"tools:parentTag="com.parkingwang.keyboard.view.InputView"><Buttonandroid:id="@+id/number_0"style="@style/PWKInputItemStyleKey"/><Buttonandroid:id="@+id/number_1"style="@style/PWKInputItemStyleKey"/><Buttonandroid:id="@+id/number_2"style="@style/PWKInputItemStyleKey"/><Buttonandroid:id="@+id/number_3"style="@style/PWKInputItemStyleKey"/><Buttonandroid:id="@+id/number_4"style="@style/PWKInputItemStyleKey"/><Buttonandroid:id="@+id/number_5"style="@style/PWKInputItemStyleKey"/><Buttonandroid:id="@+id/number_6"style="@style/PWKInputItemStyleKey"/><Buttonandroid:id="@+id/number_7"style="@style/PWKInputItemStyleKey"android:visibility="gone"/></merge>

3、键盘布局管理器

/*** 车牌号码类型对应的布局管理** @author fdw fdw628@wkhere.com*/
class LayoutManager {interface LayoutProvider {LayoutEntry get(Context ctx);}private final static String NAME_PROVINCE = "layout.province";private final static String NAME_FIRST = "layout.first.spec";private final static String NAME_LAST = "layout.last.spec";private final static String NAME_WITH_IO = "layout.with.io";private final static String NAME_WITHOUT_IO = "layout.without.io";private final static String NAME_WITHOUT_IO_BACK = "layout.without.io.back";private final Map<String, LayoutEntry> mNamedLayouts = new HashMap<>();private final List<LayoutProvider> mProviders = new ArrayList<>(5);LayoutManager() {// 省份简称布局mNamedLayouts.put(NAME_PROVINCE, createRows("京津晋冀蒙辽吉黑沪苏","浙皖闽赣鲁豫鄂湘粤桂","琼渝川贵云藏陕甘","青宁新台" + VNumberChars.MORE + "-+"));// 首位特殊字符布局mNamedLayouts.put(NAME_FIRST, createRows("1234567890","QWERTYCVBN","ASDFGHJKL","ZX民使" + VNumberChars.BACK + "-+"));// 带IO字母+数字mNamedLayouts.put(NAME_WITH_IO, createRows("1234567890","QWERTYUIOP","ASDFGHJKLM","ZXCVBN-+"));// 不带IO字母+数字 带返回按钮(军警车第三位使用)mNamedLayouts.put(NAME_WITHOUT_IO_BACK, createRows("1234567890","QWERTYUBNP","ASDFGHJKLM","ZXCV" + VNumberChars.BACK + "-+"));// 末位特殊字符mNamedLayouts.put(NAME_LAST, createRows("学警港澳航挂试超使领","1234567890","ABCDEFGHJK","WXYZ" + VNumberChars.BACK + "-+"));// 无IO字母+数字mNamedLayouts.put(NAME_WITHOUT_IO, createRows("1234567890","QWERTYUPMN","ASDFGHJKLB","ZXCV" + VNumberChars.MORE + "-+"));mProviders.add(new ProvinceLayoutProvider());mProviders.add(new FirstSpecLayoutProvider());mProviders.add(new WithIOLayoutProvider());mProviders.add(new LastSpecLayoutProvider());mProviders.add(new WithoutIOLayoutProvider());}private static LayoutEntry createRows(String... rows) {final LayoutEntry layout = new LayoutEntry(rows.length);for (String keys : rows) {layout.add(mkEntitiesOf(keys));}return layout;}/*** 返回布局对象** @param ctx Context* @return 缓存布局对象的副本*/@NonNullpublic LayoutEntry getLayout(@NonNull Context ctx) {LayoutEntry layout = new LayoutEntry();for (LayoutProvider provider : mProviders) {final LayoutEntry ret = provider.get(ctx);if (null != ret) {layout = ret;break;}}return layout.newCopy();}/*** 省份简称布局提供器。* 1. 第1位,未知类型,非特殊状态;* 2. 第1位,民用、新能源、新旧领事馆类型;* 3. 第3位,武警类型;*/final class ProvinceLayoutProvider implements LayoutProvider {@Overridepublic LayoutEntry get(Context ctx) {if (0 == ctx.selectIndex || 2 == ctx.selectIndex) {if (0 == ctx.selectIndex && NumberType.AUTO_DETECT.equals(ctx.numberType) && !ctx.reqSpecLayout) {return mNamedLayouts.get(NAME_PROVINCE);} else if (0 == ctx.selectIndex && ctx.numberType.isAnyOf(CIVIL, NEW_ENERGY, LING2012, LING2018)) {return mNamedLayouts.get(NAME_PROVINCE);} else if (2 == ctx.selectIndex && NumberType.WJ2012.equals(ctx.numberType)) {if (ctx.reqSpecLayout) {return mNamedLayouts.get(NAME_WITHOUT_IO_BACK);} else {return mNamedLayouts.get(NAME_PROVINCE);}} else {return null;}} else {return null;}}}/*** 首位特殊字符布局提供器。* 1. 第1位,未知类型,且进入特殊布局状态;* 1. 第1位,武警、军队、新旧使馆类型、民航类型;*/final class FirstSpecLayoutProvider implements LayoutProvider {@Overridepublic LayoutEntry get(Context ctx) {if (0 == ctx.selectIndex) {if (ctx.numberType.isAnyOf(WJ2012, PLA2012, SHI2012, SHI2017, AVIATION)) {return mNamedLayouts.get(NAME_FIRST);} else if (ctx.reqSpecLayout) {return mNamedLayouts.get(NAME_FIRST);} else {return null;}} else {return null;}}}/*** 带IO字母+数字布局提供器。* 1. 第4-6位;* 2. 第2位,非民航类型;* 3. 第3位,非武警类型;*/final class WithIOLayoutProvider implements LayoutProvider {@Overridepublic LayoutEntry get(Context ctx) {if (3 == ctx.selectIndex || 4 == ctx.selectIndex || 5 == ctx.selectIndex) {return mNamedLayouts.get(NAME_WITH_IO);} else if (1 == ctx.selectIndex && !AVIATION.equals(ctx.numberType)) {return mNamedLayouts.get(NAME_WITH_IO);} else if (2 == ctx.selectIndex && !WJ2012.equals(ctx.numberType)) {return mNamedLayouts.get(NAME_WITH_IO);} else {return null;}}}/*** 末位特殊字符布局提供器。* 1. 第2位,民航车牌类型;* 2. 第7位,进入特殊布局状态;* 3. 第7位,新2017式大使馆、新旧领事馆类型;*/final class LastSpecLayoutProvider implements LayoutProvider {@Overridepublic LayoutEntry get(Context ctx) {if (1 == ctx.selectIndex) {return mNamedLayouts.get(NAME_LAST);} else if (6 == ctx.selectIndex) {if (ctx.numberType.isAnyOf(SHI2017, LING2012, LING2018)) {return mNamedLayouts.get(NAME_LAST);} else if (ctx.reqSpecLayout) {return mNamedLayouts.get(NAME_LAST);} else {return null;}} else {return null;}}}/*** 无IO字符+数字布局提供器。* 1. 第7位,民用类型,非特殊布局状态;* 2. 第7位,新能源、武警、军队、旧2012式大使馆、民航;* 3. 第8位;*/final class WithoutIOLayoutProvider implements LayoutProvider {@Overridepublic LayoutEntry get(Context ctx) {if (6 == ctx.selectIndex) {if (NumberType.CIVIL.equals(ctx.numberType) && !ctx.reqSpecLayout) {return mNamedLayouts.get(NAME_WITHOUT_IO);} else if (ctx.numberType.isAnyOf(NEW_ENERGY, WJ2012, PLA2012, SHI2012, AVIATION)) {return mNamedLayouts.get(NAME_WITHOUT_IO);} else {return null;}} else if (7 == ctx.selectIndex) {return mNamedLayouts.get(NAME_WITHOUT_IO);} else {return null;}}}}

三、github开源地址

https://github.com/Frank628/android_carnum_keybord

android 车牌键盘输入法,支持新能源,警车,军车,领事馆车,特种车辆(源代码)相关推荐

  1. android 车牌键盘,支持新能源,警车,军车,领事馆车,特种车辆(源代码)

    最近在android项目中,遇到需要android车牌键盘的需求(需要支持普通车牌,新能源,警车,军车,领事馆车,教练车以及特种车辆等车牌) 一.示例图 话不多说,分享一下android车牌键盘效果图 ...

  2. android 判断键盘 输入法 是否弹出

    /*** 判断软键盘输入法是否弹出*/public boolean isKeyboardVisbility(Context context, View v) {InputMethodManager i ...

  3. android 小米键盘输入法,Facemoji输入法小米版

    Tags:手机输入法 Facemoji输入法小米版APP是能够实现小米手机完美兼容的手机表情符号键盘APP,为你集成了语音输入.滑动打字.贴纸.表情符号等各种功能,让你随时都可以创建一个更有趣.更轻松 ...

  4. android 手机键盘输入法,如何控制输入法软键盘的出现方式

    调用 open(null);/** * 当前弹出则关闭 * 否则弹出 * @param v */ @SuppressWarnings("static-access") public ...

  5. 车牌输入法 车牌号快捷输入法 支持普通车牌新能源车牌

    车牌输入法 车牌号快捷输入法 支持普通车牌&新能源车牌 效果图如下 上代码 前提:import plateInput from '@/components/uni-plate-input/un ...

  6. android 自定义车牌键盘(kotlin)

    android 自定义车牌键盘(kotlin) 前言 示例图 实现需求 定义键盘文件 核心代码 视频效果 汉字转化 项目链接 前言 平时停车缴费都要填车牌号码,就想着自己能不能也做个车牌键盘demo. ...

  7. 号牌号码识别,支持新能源车牌,离线识别,喜欢就收下吧

    号牌号码识别,支持新能源车牌,离线识别 我的项目地址: https://github.com/ccc920123/LicensePlateDemo 如果喜欢记得标星哟!!! 号牌识别,支持新能源车,离 ...

  8. vue移动端车牌号输入键盘组件封装(支持新能源车牌和uniapp)

    一.效果图          二.说明 支持点击输入框删除或输入车牌,可直接在uniapp项目中运行,将view标签换为div.并且将rpx单位换成px可在vue项目里运行 三.代码 <temp ...

  9. android 自定义键盘_Android自定义输入车牌号键盘、车牌简称,数字 ,字母键盘...

    本文来自阿钟的投稿,全文阅读大约十分钟 为了便于用户快捷的输入车牌号码便需要自定义个车牌键盘,而不是使用系统的键盘输入,上效果图: 横屏效果 图片 竖屏效果 图片 一.首先我们要来分析一下需要做哪些东 ...

  10. Android判断软键盘 输入法 是否显示

    文章目录 1.简介 2.判断原理 3.功能实现 3.文件参考 1.简介 1)判断界面上是否显示出软键盘 2)有软键盘隐藏,没有显示 2.判断原理 根据判断整体可见区域和整体界面的位置 3.功能实现 c ...

最新文章

  1. 鸿蒙系统首批更新机器,鸿蒙系统升级名单
  2. oracle win10家庭版,Windows10远程报错:由于CredSSP加密Oracle修正(ps:Win10家庭版)
  3. Ajax跨域问题的两种解决方法
  4. Linux命令之ln软链接
  5. .top域名应注意什么
  6. c++ 优先队列_什么是队列?(Python队列)
  7. 经典递归——斐波那契数列,汉诺塔
  8. C语言指针变量与一维数组
  9. 12306能删候补订单记录_12306候补购票功能在哪里怎么用 火车票候补购票使用攻略...
  10. 如何从Mac OS X连接 ×××
  11. Java写的一个简易木马(仅供自娱自乐)(基于UDP实现版)
  12. 使用Dozer进行对象copy,某些字段没有被copy的排查
  13. 国外程序员爱用苹果Mac电脑的10大理由
  14. 用实例给新手讲解易懂的RSA加密解密算法
  15. Java后端开发面试7大核心总结,为你保驾护航金九银十!
  16. 8个流水灯c语言程序按顺序亮,求由AT89S52单片机,8个LED,流水灯程序,要求八灯一盏接一盏闪烁,且循环不灭,,要求用c语...
  17. win7修复ubuntu的引导
  18. LSP(Layered Service Provider)入门的基础知识概念
  19. PCIE LANE翻转和PN翻转(lane reversal and polarity)
  20. 8.4V/12.6V/16.8V筋膜枪原理与筋膜枪方案,和筋膜枪原理图

热门文章

  1. 木子-前端-ajax传值与接收最简单的方式
  2. 无密码解锁iPhone
  3. matplotlib 绘制 虚线线型之间的间隔设置
  4. uni-app真机运行app时报错:TypeError: Cannot read property ‘call’ of undefined
  5. UT2011学习笔记
  6. 魅族手机怎么把计算机放到桌面,魅族手机怎么投屏到电脑 照片怎么传到新手机...
  7. kryo java_使用Kryo序列化任意Java对象(获取IllegalAccessError)
  8. 数据防泄露解决方案-智能制造
  9. 2022-01-24:K 距离间隔重排字符串。 给你一个非空的字符串 s 和一个整数 k,你要将这个字符串中的字母进行重新排列,使得重排后的字符串中相同字母的位置间隔距离至少为 k。 所有输入的字符串
  10. molten php 上传,molten:PHP 应用透明链路追踪工具