最近作了個關於Android設備Usb外接掃碼器的項目,在此記錄下。掃碼器有如下這兩種模式:
支持 Android 熱插拔USB掃描槍會在有EditText時,掃描槍掃描內容自動輸入到編輯框了,在沒有EditText的狀況下呢?還會響應獲焦控件的點擊事件(如Button),由於標準掃描槍掃描數據會觸發KEYCODE_ENTER鍵。android
經過USB 虛擬串口方式,這個我喜歡,但是它不支持! 項目需求:git
掃碼槍是基於鍵盤輸入的,那事件會先分發到獲取焦點的Activity、Dialog 中的,dispatchKeyEvent(KeyEvent event)
.因此很好解決了由安卓事件分發機制看,只要消費了掃碼器產生的事件,就不須要EditText,也不會觸發到其它組件了。那好,如今新的問題又來了,dispatchKeyEvent(KeyEvent event)
是按鍵事件分發的第一個要塞,並且沒辦法統一爲應用設置監聽,只能在每一個Activity、Dialog做監聽。這裏能夠基類(BaseActivity)處理掃碼器的輸入事件,也能夠經過AccessibilityService 的 onKeyEvent(KeyEvent event)
事件去處理,但無障礙輔助須要手動開啓,不太友好。
查看 KeyEvent 源碼一看繼承 InputEvent,正好能夠經過 InputDevice getDevice()
獲取輸入設備,根據輸入設備正好判斷該事件輸入掃碼槍輸入github
如下是BarCodeHelper.kt 處理掃碼器輸入事件且回調條形碼numberapi
//掃碼設備名稱 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_DEVICES fun 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 }
BaseActivityapp
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 方式註冊ide
@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 節點下post
<meta-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="" />