最近做了一个Android外接USB读卡器刷手环读取数据,模拟键盘输入事件的项目;

借鉴了https://github.com/githubRonda/BarcodeScanner

连接电子牌板子调试,可以将板子上OTG跳帽取下,然后用一根双USB口的线连接电脑就可以调试了(ps:板子上一般连接靠近网线的USB口)

因为之前公司的Android系统的电子牌的读卡器是通过串口开发的,最近由于换了电子牌的厂商,手环读取方式也更改了,无奈研究了一番,从网上找了相关文章,但是没有找到具体的,其中根据某个大神的外接扫码器的项目,结合实际终于实现了;

不管外接扫码枪还是外接读卡器,其实原理就我了解的原理是一样的,都是会将扫描到或者刷卡读取到的数据模拟成键盘输入的事件,你会发现,当Android界面有一个焦点输入框时候,扫码或者刷卡,数据都会自动填充输入框, 那问题也随之而来,我们需要做的其实就是监听按键输入事件,然后获取扫到或者读取到的数据,之后进行一些其他方面的操作.

回归正文:

 

后台无障碍服务AccessibilityService配置

import android.accessibilityservice.AccessibilityService;import android.util.Log;import android.view.KeyEvent;import android.view.accessibility.AccessibilityEvent;/** * Created by Administrator on 2019/4/22. */public class ReadCardService extends AccessibilityService {    private static final String TAG = ReadCardService.class.getSimpleName();    @Override    public void onAccessibilityEvent(AccessibilityEvent event) {        Log.e(TAG, "onAccessibilityEvent --> " + event);    }    @Override    public void onInterrupt() {        Log.e(TAG, "onInterrupt");    }    /**     * 复写这个方法可以捕获按键事件     *     * @param event     * @return     */    @Override    protected boolean onKeyEvent(KeyEvent event) {        int keyCode = event.getKeyCode();        Log.e(TAG, "keyEvent:" + event + "keyCode: " + keyCode + "char: " + KeyEvent.keyCodeToString(keyCode));        return super.onKeyEvent(event);    }}

在AndroidManifest.xml文件中记得配置

                                                            

res目录下创建xml文件夹,并在xml文件夹内创建accessibility.xml文件内容如下

<?xml version="1.0" encoding="utf-8"?>accessibility_description在res的values文件夹内strings中设置你想要说明的,例如xxx按键监听的无障碍辅助服务

ReadCardUtils工具类

import android.content.Context;import android.content.res.Configuration;import android.os.Handler;import android.util.Log;import android.view.InputDevice;import android.view.KeyEvent;/** * Created by Administrator on 2019/4/22. * * 使用说明: *  * 1. 在Activity中先创建ReadCardUtils对象,并设置扫码成功监听器: setReadSuccessListener() [一般在onCreate()方法中初始化] *  * 2. 接着在Activity#dispatchKeyEvent() 或者 Activity#onKeyDown() 中调用本类中的resolveKeyEvent()方法。当扫码结束之后,会自动回调第一步设置的监听器中的方法 *  * *  * 原理分析: *  * 1. 扫码枪就是一个外部的输入设备(和键盘一样)。扫码的时候,就是在极短的时间内输入了一系列的数字或字母 *  * 2. 这样就可以在键盘事件中抓捕这些输入的字符,但是又会产生一个问题(快速扫两次的情形):在键盘事件中应该抓捕多少个字符呢?即一个条码应该在哪个位置结束呢? (有的扫码枪会以一个回车或者换行作为一次扫码的结束符,但是有的就纯粹的是一系列的条码。这个得需要设置) *  * 所以为了兼容性,应当是当短时间内不再输入字符的时候,就表示扫码已结束。这样只能定性描述,不能定量,只能自己在程序中用一个具体的数字来表示这个“短时间”,eg:500ms。(如果每个条码结束的时候都有一个结束符那该多好,直接判断这个结束符,就可以知道当前扫码已完成) *  * *  * 接下来就产生了ReadCardUtils这个类。 *  * 核心原理就一句话:在Activity的键盘监听事件中,每抓捕到一个字符的时候,就先向 Handler 一次一个runnable对象,再延迟500ms发送一个runnable. 这样若两个输入字符的间隔时间超过了500ms,则视为两次扫码 * */public class ReadCardUtils {    private static final String TAG = ReadCardUtils.class.getSimpleName();    // 若500ms之内无字符输入,则表示扫码完成. (若觉得时间还长,则可以设置成更小的值)    private final static long MESSAGE_DELAY = 500;    private boolean mCaps;//大写或小写    private StringBuilder mResult = new StringBuilder();//扫码内容    private OnReadSuccessListener mOnReadSuccessListener;    private Handler mHandler = new Handler();    private final Runnable mReadingEndRunnable = new Runnable() {        @Override        public void run() {            performScanSuccess();        }    };    //调用回调方法    private void performScanSuccess() {        String barcode = mResult.toString();        //Log.i(TAG, "performScanSuccess -> barcode: "+barcode);        if (mOnReadSuccessListener != null) {            mOnReadSuccessListener.onScanSuccess(barcode);        }        mResult.setLength(0);    }    //key事件处理    public void resolveKeyEvent(KeyEvent event) {        int keyCode = event.getKeyCode();        checkLetterStatus(event);//字母大小写判断        Log.w(TAG, "keyEvent:" + event + "keyCode: " + keyCode + "char: " + KeyEvent.keyCodeToString(keyCode));        if (event.getAction() == KeyEvent.ACTION_DOWN) {            char aChar = getInputCode(event);            Log.w(TAG, "aChar: " + aChar);            if (aChar != 0) {                mResult.append(aChar);            }            if (keyCode == KeyEvent.KEYCODE_ENTER) {                //若为回车键,直接返回                mHandler.removeCallbacks(mReadingEndRunnable);                mHandler.post(mReadingEndRunnable);            } else {                //延迟post,若500ms内,有其他事件                mHandler.removeCallbacks(mReadingEndRunnable);                mHandler.postDelayed(mReadingEndRunnable, MESSAGE_DELAY);            }        }    }    //检查shift键    private void checkLetterStatus(KeyEvent event) {        int keyCode = event.getKeyCode();        if (keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT || keyCode == KeyEvent.KEYCODE_SHIFT_LEFT) {            if (event.getAction() == KeyEvent.ACTION_DOWN) {                //按着shift键,表示大写                mCaps = true;            } else {                //松开shift键,表示小写                mCaps = false;            }        }    }    //获取扫描内容    private char getInputCode(KeyEvent event) {        int keyCode = event.getKeyCode();        char aChar;        if (keyCode >= KeyEvent.KEYCODE_A && keyCode <= KeyEvent.KEYCODE_Z) {            //字母            aChar = (char) ((mCaps ? 'A' : 'a') + keyCode - KeyEvent.KEYCODE_A);        } else if (keyCode >= KeyEvent.KEYCODE_0 && keyCode <= KeyEvent.KEYCODE_9) {            //数字            aChar = (char) ('0' + keyCode - KeyEvent.KEYCODE_0);        } else {            //其他符号            switch (keyCode) {                case KeyEvent.KEYCODE_PERIOD:                    aChar = '.';                    break;                case KeyEvent.KEYCODE_MINUS:                    aChar = mCaps ? '_' : '-';                    break;                case KeyEvent.KEYCODE_SLASH:                    aChar = '/';                    break;                case KeyEvent.KEYCODE_BACKSLASH:                    aChar = mCaps ? '|' : '\\';                    break;                default:                    aChar = 0;                    break;            }        }        return aChar;    }    /**     * 检测输入设备是否是读卡器     *     * @param context     * @return 是的话返回true,否则返回false     */    public static boolean isInputFromReader(Context context, KeyEvent event) {        if (event.getDevice() == null) {            return false;        }//        event.getDevice().getControllerNumber();        if (event.getKeyCode() == KeyEvent.KEYCODE_BACK || event.getKeyCode() == KeyEvent.KEYCODE_VOLUME_DOWN || event.getKeyCode() == KeyEvent.KEYCODE_VOLUME_UP) {            //实体按键,若按键为返回、音量加减、返回false            return false;        }        if (event.getDevice().getSources() == (InputDevice.SOURCE_KEYBOARD | InputDevice.SOURCE_DPAD | InputDevice.SOURCE_CLASS_BUTTON)) {            //虚拟按键返回false            return false;        }        Configuration cfg = context.getResources().getConfiguration();        return cfg.keyboard != Configuration.KEYBOARD_UNDEFINED;    }    public interface OnReadSuccessListener {        void onScanSuccess(String barcode);    }    public void setReadSuccessListener(OnReadSuccessListener onReadSuccessListener) {        mOnReadSuccessListener = onReadSuccessListener;    }    public void removeScanSuccessListener() {        mHandler.removeCallbacks(mReadingEndRunnable);        mOnReadSuccessListener = null;    }}

在你的MainActivity类中

声明读取工具类ReadCardUtils服务,并在合适的位置初始化

    //U口读卡器,类似于外接键盘    private ReadCardUtils readCardUtils;

初始化

    //读卡器声明    readCardUtils = new ReadCardUtils();    initCardReader();
 /**     * 读卡器初始化     */    private void initCardReader() {        readCardUtils.setReadSuccessListener(new ReadCardUtils.OnReadSuccessListener() {            @Override            public void onScanSuccess(String barcode) {                Log.e(TAG, "barcode: " + barcode);            }        });    }    @Override    public boolean dispatchKeyEvent(KeyEvent event) {        if (ReadCardUtils.isInputFromReader(this, event)) {            if (readCardUtils != null){                readCardUtils.resolveKeyEvent(event);            }        }        return super.dispatchKeyEvent(event);    }
    @Override    public boolean onKeyDown(int keyCode, KeyEvent event) {        return super.onKeyDown(keyCode, event);    }    @Override    protected void onDestroy() {        readCardUtils.removeScanSuccessListener();        readCardUtils = null;        super.onDestroy();    }

 

更多相关文章

  1. Android:自定义输入法(输入密码时防止第三方窃取)
  2. Android客户端请求服务端资源(HttpURLConnection和输入流实现)
  3. android:imeOptions属性详解以及无效处理
  4. android 中用代码模拟发送按键
  5. MediaButtonReceiver---独特的媒体广播接收器
  6. Android(安卓)EditText 限制只能输入指定范围的数字
  7. 实现Android监控任意控件或按键双击事件方法
  8. macOS中配置Flutter开发环境(使用AndroidStudio开发)傻瓜版教程
  9. Android搜索框输入内容点击键盘的搜索按钮进行搜索

随机推荐

  1. 汇总:Linux文件管理的50个命令
  2. 你是怎么看待SaaS的?
  3. Python 循环语句
  4. Shell的18条常用命令整理
  5. 页表 段号 页号 分段式 分页式 段页式
  6. 我的开源项目——Windows PE和Linux ELF
  7. 2021-03-17:手写代码:单链表插入排序。
  8. 3-17(排序)
  9. 时隔三年再更新!绝对良心工具,免费好用
  10. 3.17 c语言自定义函数