android單屏機(jī),通過(guò)掃碼槍掃描二維碼的場(chǎng)景非常多,掃碼槍的種類也有藍(lán)牙、USB、串口等等
目前USB的掃碼槍主流的就是以下兩種
1、USB HID-KBW:掃碼器會(huì)將掃描出來(lái)的內(nèi)容轉(zhuǎn)化為鍵盤事件,就是Android中KeyEvent里面對(duì)應(yīng)的常量(KeyEvent.KEYCODE_*)。
2、USB 虛擬串口:可使用android-serialport-api 連接到UsbDevice進(jìn)行通信,讀取數(shù)據(jù)。(設(shè)備要支持串口)
支持 Android 熱插拔USB掃描槍會(huì)在有EditText時(shí),掃描槍掃描內(nèi)容自動(dòng)輸入到編輯框了,但是有很多輸入法兼容的問(wèn)題,比如搜狗輸入法識(shí)別到HID設(shè)備時(shí)會(huì)隱藏?zé)o法彈出,如果輸入法切換成中文時(shí)會(huì)輸入中文等等。
通過(guò)串口的方式直接獲取原始數(shù)據(jù),不再跟輸入法產(chǎn)生沖突,可惜設(shè)備是USB HID的,通過(guò)大量的嘗試(包括USB虛擬串口)都不支持(對(duì)串口不了解的同學(xué)可以先看看這篇文章上半年最好的Android串口開發(fā)入門指南 - 簡(jiǎn)書 )
掃碼槍是基于鍵盤輸入的,嘗試從獲取焦點(diǎn)的Activity中的dispatchKeyEvent(KeyEvent event)進(jìn)行攔截,可惜只能解決掉中文的問(wèn)題,事件還是先走到輸入法才能回到Activity。于是強(qiáng)大的AccessibilityService就上場(chǎng)了,使用AccessibilityService可以優(yōu)先獲取到鍵盤事件。
使用強(qiáng)大的AccessibilityService(Google為了讓Android系統(tǒng)更實(shí)用,為用戶提供了無(wú)障礙輔助服務(wù)),但需要到系統(tǒng)設(shè)置->無(wú)障礙->服務(wù) 開啟當(dāng)前服務(wù)。對(duì)AccessibilityService不了解的同學(xué)看看http://www.lxweimin.com/p/4cd8c109cdfb
廢話不多說(shuō)看實(shí)現(xiàn)步驟
1、先創(chuàng)建掃碼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){
//這里通過(guò)回調(diào)的方式將事件傳出去統(tǒng)一處理
//返回true事件就會(huì)攔截不會(huì)繼續(xù)傳遞
return onKeyEvent.onKeyEvent(event);
}
return super.onKeyEvent(event);
}
/**
* 設(shè)置監(jiān)聽
* @param onKeyEvent
*/
public static void setOnKeyEvent(OnKeyEvent onKeyEvent){
ScanService.onKeyEvent=onKeyEvent;
}
public interface OnKeyEvent{
boolean onKeyEvent(KeyEvent event);
}
}
2、創(chuàng)建好自己的ScanService后需要在manifest中進(jìn)行注冊(cè)
<service
android:name="包名.service.ScanService"
android:enabled="true"
android:exported="true"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService"/>
</intent-filter>
<meta-data
android:name="android.accessibilityservice"
android:resource="@xml/accessibility"
/>
</service>
創(chuàng)建android:resource需要用到的xml ,在res下新建xml文件夾,新建accessibility.xml
<?xml version="1.0" encoding="utf-8"?>
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
android:accessibilityEventTypes="typeAllMask"
android:accessibilityFeedbackType="feedbackGeneric"
android:accessibilityFlags="flagRequestFilterKeyEvents"
android:canRetrieveWindowContent="true"
android:notificationTimeout="100"
android:canRequestFilterKeyEvents="true"
android:description="@string/accessibility_description"
android:packageNames="包名" />
android:description指定一個(gè)String作為描述文案
<string name="accessibility_description">這里是描述輔助功能的文案</string>
到此為止AccessibilityService就配置好了,你的應(yīng)用就會(huì)出現(xiàn)在系統(tǒng)設(shè)置->輔助功能列表里,只需要手動(dòng)在設(shè)置中打開輔助功能,掃碼槍的鍵盤事件就會(huì)觸發(fā)ScanService的onKeyEvent
接下來(lái)是對(duì)事件的處理
1、過(guò)濾非掃碼槍的設(shè)備
/**
* 檢測(cè)輸入設(shè)備是否是掃碼器
*
* @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) {
//實(shí)體按鍵,若按鍵為返回、音量加減、返回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();
//做相應(yīng)處理....
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內(nèi),有其他事件
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;
}
}
}
//獲取掃描內(nèi)容
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) {
//數(shù)字
aChar = (char) ('0' + keyCode - KeyEvent.KEYCODE_0);
} else if (keyCode == KeyEvent.KEYCODE_ENTER) {
aChar = 0;
} else {
//其他符號(hào)
aChar = (char) event.getUnicodeChar();
}
return aChar;
}
掃描完成,獲取掃描的數(shù)據(jù)后,自己想怎么處理就怎么處理
最后附上一些工具類
跳轉(zhuǎn)到系統(tǒng)輔助功能頁(yè)
/**
* 打開設(shè)置-輔助功能頁(yè)
* @param context
*/
public void openAccessibilitySetting(Context context){
context.startActivity(new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS));
}
判斷當(dāng)前應(yīng)用的輔助功能在設(shè)置中是否打開
/**
*
* @param context
* @return true輔助功能開 false輔助功能關(guān)
*/
public boolean isAccessibilitySettingsOn(Context context) {
int accessibilityEnabled = 0;
final String service = context.getPackageName() + "/" + ScanService.class.getCanonicalName();
try {
//獲取setting里輔助功能的開啟狀態(tài)
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) {
//獲取輔助功能里所有開啟的服務(wù) 包名列表
String settingValue = Settings.Secure.getString(
context.getApplicationContext().getContentResolver(),
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
if (settingValue != null) {
//轉(zhuǎn)換程集合
mStringColonSplitter.setString(settingValue);
while (mStringColonSplitter.hasNext()) {
String accessibilityService = mStringColonSplitter.next();
//判斷當(dāng)前包名是否在服務(wù)集合里
if (accessibilityService.equalsIgnoreCase(service)) {
return true;
}
}
}
}
return false;
}
完成。