android单屏机,通过扫码枪扫描二维码的场景非常多,扫码枪的种类也有蓝牙、USB、串口等等


目前USB的扫码枪主流的就是以下两种

1、USB HID-KBW:扫码器会将扫描出来的内容转化为键盘事件,就是Android中KeyEvent里面对应的常量(KeyEvent.KEYCODE_*)。

2、USB 虚拟串口:可使用android-serialport-api 连接到UsbDevice进行通信,读取数据。(设备要支持串口)


支持 Android 热插拔USB扫描枪会在有EditText时,扫描枪扫描内容自动输入到编辑框了,但是有很多输入法兼容的问题,比如搜狗输入法识别到HID设备时会隐藏无法弹出,如果输入法切换成中文时会输入中文等等。

通过串口的方式直接获取原始数据,不再跟输入法产生冲突,可惜设备是USB HID的,通过大量的尝试(包括USB虚拟串口)都不支持(对串口不了解的同学可以先看看这篇文章上半年最好的Android串口开发入门指南 - )

扫码枪是基于键盘输入的,尝试从获取焦点的Activity中的dispatchKeyEvent(KeyEvent event)进行拦截,可惜只能解决掉中文的问题,事件还是先走到输入法才能回到Activity。于是强大的AccessibilityService就上场了,使用AccessibilityService可以优先获取到键盘事件。

使用强大的AccessibilityService(Google为了让Android系统更实用,为用户提供了无障碍辅助服务),但需要到系统设置->无障碍->服务 开启当前服务。对AccessibilityService不了解的同学看看http://www.jianshu.com/p/4cd8c109cdfb

废话不多说看实现步骤

1、先创建扫码Service直接继承AccessibilityService就OK

public class ScanService extends AccessibilityService {    private static OnKeyEvent onKeyEvent;    @Override    public int onStartCommand(Intent intent, int flags, int startId) {        return START_STICKY;    }    @Override    public void onAccessibilityEvent(AccessibilityEvent event) {    }    @Override    public void onInterrupt() {    }    @Override    protected boolean onKeyEvent(KeyEvent event) {        if(onKeyEvent!=null){          //这里通过回调的方式将事件传出去统一处理          //返回true事件就会拦截不会继续传递           return onKeyEvent.onKeyEvent(event);        }        return super.onKeyEvent(event);    }    /**     * 设置监听     * @param onKeyEvent     */    public static void setOnKeyEvent(OnKeyEvent onKeyEvent){        ScanService.onKeyEvent=onKeyEvent;    }    public interface OnKeyEvent{        boolean onKeyEvent(KeyEvent event);    }}

2、创建好自己的ScanService后需要在manifest中进行注册

                                                     

创建android:resource需要用到的xml ,在res下新建xml文件夹,新建accessibility.xml

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

android:description指定一个String作为描述文案

这里是描述辅助功能的文案

到此为止AccessibilityService就配置好了,你的应用就会出现在系统设置->辅助功能列表里,只需要手动在设置中打开辅助功能,扫码枪的键盘事件就会触发ScanService的onKeyEvent

接下来是对事件的处理
1、过滤非扫码枪的设备

  /**     * 检测输入设备是否是扫码器     *     * @param context     * @return 是的话返回true,否则返回false     */    public boolean isInputFromScanner(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;    }

2、处理事件

Runnable mScanningFishedRunnable = new Runnable() {            @Override            public void run() {                String code = mStringBufferResult.toString();                //做相应处理....                mStringBufferResult.setLength(0);            }        };/**     * 扫码枪事件解析     *     * @param event     */    public void analysisKeyEvent(KeyEvent event) {        int keyCode = event.getKeyCode();        //字母大小写判断        checkLetterStatus(event);        if (event.getAction() == KeyEvent.ACTION_DOWN) {            char aChar = getInputCode(event);//            char aChar = (char) event.getUnicodeChar();            if (aChar != 0) {                mStringBufferResult.append(aChar);            }            if (keyCode == KeyEvent.KEYCODE_ENTER) {                //若为回车键,直接返回                mHandler.removeCallbacks(mScanningFishedRunnable);                mHandler.post(mScanningFishedRunnable);            } else {                //延迟post,若500ms内,有其他事件                mHandler.removeCallbacks(mScanningFishedRunnable);                mHandler.postDelayed(mScanningFishedRunnable, 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 if (keyCode == KeyEvent.KEYCODE_ENTER) {            aChar = 0;        } else {            //其他符号            aChar = (char) event.getUnicodeChar();        }        return aChar;    }

扫描完成,获取扫描的数据后,自己想怎么处理就怎么处理

最后附上一些工具类
跳转到系统辅助功能页

  /**     * 打开设置-辅助功能页     * @param context     */    public void openAccessibilitySetting(Context context){        context.startActivity(new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS));    }

判断当前应用的辅助功能在设置中是否打开

/**     *     * @param context     * @return true辅助功能开 false辅助功能关     */    public boolean isAccessibilitySettingsOn(Context context) {        int accessibilityEnabled = 0;        final String service = context.getPackageName() + "/" + ScanService.class.getCanonicalName();        try {            //获取setting里辅助功能的开启状态            accessibilityEnabled = Settings.Secure.getInt(                    context.getApplicationContext().getContentResolver(),                    android.provider.Settings.Secure.ACCESSIBILITY_ENABLED);        } catch (Settings.SettingNotFoundException e) {        }        TextUtils.SimpleStringSplitter mStringColonSplitter = new TextUtils.SimpleStringSplitter(':');        if (accessibilityEnabled == 1) {            //获取辅助功能里所有开启的服务 包名列表            String settingValue = Settings.Secure.getString(                    context.getApplicationContext().getContentResolver(),                    Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);            if (settingValue != null) {                //转换程集合                mStringColonSplitter.setString(settingValue);                while (mStringColonSplitter.hasNext()) {                    String accessibilityService = mStringColonSplitter.next();                    //判断当前包名是否在服务集合里                    if (accessibilityService.equalsIgnoreCase(service)) {                        return true;                    }                }            }        }        return false;    }

完成。

更多相关文章

  1. Android(安卓)之 下拉框(Spinner)的使用
  2. 1.5 Button
  3. Android(安卓)自定义RecyclerView.OnScrollListener,实现上拉分页
  4. Android(安卓)RecyclerView简单运用
  5. Android(安卓)串口通信编程及串口协议分析
  6. Android中的 View绘制流程及事件分发
  7. 基于Android(安卓)XML解析与保存的实现
  8. View事件传递分析
  9. Android的Touch系统简介(一)

随机推荐

  1. SharePreferences实现
  2. android 学习之---Android之GoogleMap(转
  3. Android home键和back键区别
  4. Android中使用SVG矢量图打造多边形图形框
  5. android重写finish实现是否退出提示功能
  6. Android UI 设计秘笈
  7. Android(安卓)Logcat输出为何能自动换行
  8. Android Bluetooth蓝牙开发:Bluetooth蓝牙
  9. Android(安卓)Volley 框架的使用(一)
  10. Android采用操作xml