最近做了个关于Android设备Usb外接扫码器的项目,在此记录下。扫码器有以下这两种模式:

  • USB HID-KBW:扫码器会将扫描出来的内容转化为键盘事件,就是Android中就是KeyEvent里面对应的常量(0 = KeyEvent.KEYCODE_0)。
  • USB 虚拟串口:可使用android-serialport-api 连接到UsbDevice进行通信,读取数据。(设备要支持串口)

支持 Android 热插拔USB扫描枪会在有EditText时,扫描枪扫描内容自动输入到编辑框了,在没有EditText的情况下呢?还会响应获焦控件的点击事件(如Button),因为标准扫描枪扫描数据会触发KEYCODE_ENTER键。

通过USB 虚拟串口方式,这个我喜欢,可是它不支持! 项目需求:

  • 扫码枪扫商品条形码时返回内容(通常一串数字),作为购买时唯一标识

扫码枪是基于键盘输入的,那事件会先分发到获取焦点的Activity、Dialog 中的,dispatchKeyEvent(KeyEvent event) .所以很好解决了由安卓事件分发机制看,只要消费了扫码器产生的事件,就不需要EditText,也不会触发到其它组件了。那好,现在新的问题又来了,dispatchKeyEvent(KeyEvent event) 是按键事件分发的第一个要塞,而且没办法统一为应用设置监听,只能在每个Activity、Dialog作监听。这里可以基类(BaseActivity)处理扫码器的输入事件,也可以通过AccessibilityService 的 onKeyEvent(KeyEvent event) 事件去处理,但无障碍辅助需要手动开启,不太友好。
查看 KeyEvent 源码一看继承 InputEvent,正好可以通过 InputDevice getDevice() 获取输入设备,根据输入设备正好判断该事件输入扫码枪输入

以下是BarCodeHelper.kt 处理扫码器输入事件且回调条形码number

//扫码设备名称const val BARCODE_DEVICES = "Barcode Reader"var scannerResult = StringBuilder()fun hasBarcodeInputDeviceExist(): Boolean {    InputDevice.getDeviceIds().forEach {        val name = InputDevice.getDevice(it).name.trim()        Log.i("InputDevice", name)        if (BARCODE_DEVICES == name) {            return true        }    }    return false}fun KeyEvent.isBarcodeKeyEvent() = this.device.name.trim() == BARCODE_DEVICESfun Activity.transformBarCodeKeyEvent(event: KeyEvent, listener: (result: String) -> Unit): Boolean {    if (event.action == KeyEvent.ACTION_DOWN && event.repeatCount == 0) {        val keyCode = event.keyCode        if (keyCode >= KeyEvent.KEYCODE_0 && keyCode <= KeyEvent.KEYCODE_9) {            scannerResult.append(keyCode - KeyEvent.KEYCODE_0)            return true        }        if (keyCode == KeyEvent.KEYCODE_ENTER) {            listener.invoke(scannerResult.toString())            scannerResult = StringBuilder()            return true        }    }    return false}fun Dialog.transformBarCodeKeyEvent(event: KeyEvent, listener: (result: String) -> Unit): Boolean {    if (event.action == KeyEvent.ACTION_DOWN && event.repeatCount == 0) {        val keyCode = event.keyCode        if (keyCode >= KeyEvent.KEYCODE_0 && keyCode <= KeyEvent.KEYCODE_9) {            scannerResult.append(keyCode - KeyEvent.KEYCODE_0)            return true        }        if (keyCode == KeyEvent.KEYCODE_ENTER) {            listener.invoke(scannerResult.toString())            scannerResult = StringBuilder()            return true        }    }    return false}

BaseActivity

    override fun dispatchKeyEvent(event: KeyEvent): Boolean {        //多数activity 不需要扫码输入,只要是扫码设备事件都消费掉,以防止触碰到控件        return if (event.isBarcodeKeyEvent()) {            this.transformBarCodeKeyEvent(event) { barCode ->                Log.i("Barcode", "barCode: " + barCode)                //EventBus                EventBus.getDefault().post(barCode, PRODUCT_BAR_CODE_EVENT)            }        } else super.dispatchKeyEvent(event)    }

另外你要是采取AccessibilityService 方式的话,又通过以下方式去设置的话,onKeyEvent(KeyEvent event)不回调,只能通过xml 方式注册

 @Override    protected void onServiceConnected() {        super.onServiceConnected();        Log.v(TAG, "on Service Connected");        AccessibilityServiceInfo info = new AccessibilityServiceInfo();        info.packageNames = null;        info.eventTypes = AccessibilityEvent.TYPES_ALL_MASK;        info.notificationTimeout = 0;        info.feedbackType = AccessibilityEvent.TYPES_ALL_MASK;        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {            info.flags = AccessibilityServiceInfo.FLAG_REQUEST_FILTER_KEY_EVENTS;        }        setServiceInfo(info);        System.out.println(getServiceInfo());    }

这里meta-data 只能写在 service 节点下

data                android:name="android.accessibilityservice"                android:resource="@xml/serviceconfig" />
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"    android:accessibilityEventTypes="typeAllMask"    android:accessibilityFeedbackType="feedbackAllMask"    android:accessibilityFlags="flagRequestFilterKeyEvents"    android:canRequestFilterKeyEvents="true"    android:canRetrieveWindowContent="true"    android:description="@string/app_name"    android:notificationTimeout="100"    android:packageNames="" />

更多相关文章

  1. 【Android - 进阶】之事件分发机制
  2. Android事件分发机制完全解析(终极版二)
  3. Android事件分发机制和一些疑问
  4. 2013阿里技术嘉年华:Android设备体验优化
  5. Android查询:模拟键盘鼠标事件(adb shell 实现)
  6. Android studio项目不能编译,提示设备版本过低
  7. Android Touch 事件的分发和消费机制
  8. android中炫酷划屏事件及sqlite全部操作Demo(1)
  9. Android点击事件的四种写法

随机推荐

  1. 想抢先体验Android操作系统的魅力吗?那就
  2. Android SDK 1.5中文版 (Application基础
  3. android之webview无网络情况下简单处理
  4. Android 命名规范 (提高代码可以读性)
  5. [置顶] 成功为Android系统配上了GNU开发
  6. Android 二维码开发功能实现(四)------基
  7. Android开发实践:在任意目录执行NDK编译
  8. Android的View体系(三):View坐标以及方法说
  9. Android之替换APP字体——Typeface
  10. Android传感器编程实例开发――三轴数据