最近做了一個(gè)關(guān)于Android設(shè)備Usb外接掃碼器的項(xiàng)目螟炫,在此記錄下根穷。
言歸正傳柏靶,首先掃碼器有這兩種模式:USB HID-KBW物邑,USB 虛擬串口溜哮。
USB HID-KBW:掃碼器會(huì)將掃描出來的內(nèi)容模擬成鍵盤事件,就是Android中就是KeyEvent里面對(duì)應(yīng)的常量(比如:KeyEvent.KEYCODE_A 表示小寫字母a色解,如果要表示大寫字母A茂嗓,就會(huì)組合KeyEvent.KEYCODE_SHIFT_LEFT或者KeyEvent.KEYCODE_SHIFT_RIGHT + KeyEvent.KEYCODE_A)。
這種模式優(yōu)點(diǎn)是使用簡(jiǎn)單科阎,只要有焦點(diǎn)的文本輸入框述吸,系統(tǒng)會(huì)自動(dòng)將掃碼出的內(nèi)容填充到文本框內(nèi),缺點(diǎn)是不支持中文!USB 虛擬串口:這種模式相對(duì)來說復(fù)雜點(diǎn)蝌矛,但也比較簡(jiǎn)單道批,Android系統(tǒng)屏蔽了底層串口通信(需要了解Android串口通信的可以看下android-serialport-api ),直接使用UsbDevice進(jìn)行通信,連接到UsbDevice入撒,讀取數(shù)據(jù)隆豹,好處是支持中文掃碼。
上代碼:
USB HID-KBW茅逮,這種模式我們可以自定義EditText對(duì)內(nèi)容進(jìn)行攔截璃赡,過濾出我們想要的內(nèi)容或者觸發(fā)我們的業(yè)務(wù)邏輯。自定義EditText很簡(jiǎn)單献雅,初始化的時(shí)候設(shè)置調(diào)用setOnKeyListener方法碉考,設(shè)置OnKeyListener,通過onKey方法返回true來攔截鍵盤事件挺身,至于攔截到的鍵盤事件通過KeyEvent的對(duì)應(yīng)常量轉(zhuǎn)換成相應(yīng)的字符(注意這里需要讓EditText獲取到焦點(diǎn)豆励,不然KeyEvent不會(huì)觸發(fā))。
public class CustomEditText extends android.support.v7.widget.AppCompatEditText {
private ScanCodeListener codeListener;
private StringBuffer buffer = new StringBuffer();
public CustomEditText(Context context) {
super(context);
init();
}
public CustomEditText(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public CustomEditText(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private int preKeyCode = -1;
private void init() {
setOnKeyListener(new OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_UP) {
Log.d("ooo", "Code:" + keyCode + ", display int:" + (int) event.getDisplayLabel() + ", char:" + event.getDisplayLabel());
if (keyCode == KeyEvent.KEYCODE_ENTER) {
String str = buffer.toString();
buffer.setLength(0);
preKeyCode = -1;
if (codeListener != null) {
codeListener.handleCode(str);
}
} else if (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT) {
preKeyCode = KeyEvent.KEYCODE_SHIFT_RIGHT;
} else {
if (preKeyCode == KeyEvent.KEYCODE_SHIFT_RIGHT) {
String shiftChar = SHIFT_KEY_MAP.get(keyCode);
if (shiftChar != null) {
buffer.append(shiftChar);
}
} else {
char label = event.getDisplayLabel();
if (label != 0) {
buffer.append(toLowCase(label));
}
}
preKeyCode = -1;
}
}
return true;
}
});
}
private char toLowCase(char c) {
if (c >= 'A' && c <= 'Z') {
c += 32;
return c;
}
return c;
}
public void setScanCodeListener(ScanCodeListener codeListener) {
this.codeListener = codeListener;
}
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
return super.dispatchKeyEvent(event);
}
@Override
public boolean dispatchKeyEventPreIme(KeyEvent event) {
boolean value = super.dispatchKeyEventPreIme(event);
return value;
}
@Override
public void setText(CharSequence text, BufferType type) {
super.setText(text, type);
}
@Override
public void setOnKeyListener(OnKeyListener l) {
super.setOnKeyListener(l);
}
public interface ScanCodeListener {
void handleCode(String code);
}
static SparseArray<String> SHIFT_KEY_MAP = new SparseArray<>();
static {
SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_GRAVE, "~");
SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_0, ")");
SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_1, "!");
SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_2, "@");
SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_3, "#");
SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_4, "$");
SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_5, "%");
SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_6, "^");
SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_7, "&");
SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_8, "*");
SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_9, ")");
SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_MINUS, "_");
SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_EQUALS, "+");
SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_LEFT_BRACKET, "{");
SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_RIGHT_BRACKET, "}");
SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_BACKSLASH, "|");
SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_SEMICOLON, ":");
SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_APOSTROPHE, "\"");
SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_COMMA, "<");
SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_PERIOD, ">");
SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_SLASH, "?");
SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_A, "A");
SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_B, "B");
SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_C, "C");
SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_D, "D");
SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_E, "E");
SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_F, "F");
SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_G, "G");
SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_H, "H");
SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_I, "I");
SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_J, "J");
SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_K, "K");
SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_L, "L");
SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_M, "M");
SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_N, "N");
SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_O, "O");
SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_P, "P");
SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_Q, "Q");
SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_R, "R");
SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_S, "S");
SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_T, "T");
SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_U, "U");
SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_V, "V");
SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_W, "W");
SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_X, "X");
SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_Y, "Y");
SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_Z, "Z");
}
}
USB 虛擬串口瞒渠,這種模式稍稍復(fù)雜點(diǎn)良蒸,要自己去連接USB設(shè)備,讀取數(shù)據(jù)伍玖,其實(shí)也很簡(jiǎn)單
- 找到設(shè)備
- 設(shè)備授權(quán)
- 連接設(shè)備
- 讀取數(shù)據(jù)
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
public static final String ACTION_DEVICE_PERMISSION = "com.demo.USB_PERMISSION";
private final int MY_PRODUCT_ID = 33286;//產(chǎn)品ID嫩痰,根據(jù)你的設(shè)備修改
private final int MY_VENDOR_ID = 7851; //廠商ID,根據(jù)你的設(shè)備修改
private UsbManager usbManager;
private TextView textView;
private UsbEndpoint inUsbEndpoint;
private UsbEndpoint outUsbEndpoint;
private UsbInterface mUsbInterface;
private UsbDeviceConnection mUsbDeviceConnection;
private Thread thread;
private Buff buff = new Buff(); //負(fù)責(zé)分割掃碼數(shù)據(jù)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
usbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
findViewById(R.id.list_usb_device).setOnClickListener(this);
findViewById(R.id.connect).setOnClickListener(this);
findViewById(R.id.read).setOnClickListener(this);
findViewById(R.id.send).setOnClickListener(this);
findViewById(R.id.clear).setOnClickListener(this);
textView = (TextView) findViewById(R.id.device_info);
}
protected void onResume() {
super.onResume();
IntentFilter usbFilter = new IntentFilter();
usbFilter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
usbFilter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
usbFilter.addAction(ACTION_DEVICE_PERMISSION);
registerReceiver(mUsbReceiver, usbFilter);
}
@Override
protected void onPause() {
super.onPause();
unregisterReceiver(mUsbReceiver);
}
private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action)) {
Log.d("xxx", "ACTION_USB_DEVICE_ATTACHED");
} else if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) {
Log.d("xxx", "ACTION_USB_DEVICE_DETACHED");
} else if (ACTION_DEVICE_PERMISSION.equals(action)) {
UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
Toast.makeText(MainActivity.this, "已授權(quán)回調(diào)", Toast.LENGTH_SHORT).show();
if (device != null) {
initCommunication(device);
}
} else {
Toast.makeText(MainActivity.this, "未授權(quán)回調(diào)", Toast.LENGTH_SHORT).show();
}
}
}
};
@Override
public void onClick(View v) {
if (v.getId() == R.id.list_usb_device) {
HashMap<String, UsbDevice> deviceList = usbManager.getDeviceList();
if (deviceList != null) {
Set<String> keySet = deviceList.keySet();
StringBuffer stringBuffer = new StringBuffer();
for (String key : keySet) {
stringBuffer.append(deviceList.get(key).toString() + "\n");
stringBuffer.append("\n\n");
}
textView.setText(stringBuffer.toString());
} else {
textView.setText("沒有設(shè)備");
}
} else if (v.getId() == R.id.connect) {
HashMap<String, UsbDevice> deviceList = usbManager.getDeviceList();
if (deviceList.size() > 0) {
for (UsbDevice device : deviceList.values()) {
if (MY_VENDOR_ID == device.getVendorId() && MY_PRODUCT_ID == device.getProductId()) {
if (usbManager.hasPermission(device)) {
initCommunication(device);
continue;
} else {
requestUsbPermission(device);
}
break;
}
}
}
} else if (v.getId() == R.id.read) {
startRead();
} else if (v.getId() == R.id.send) {
sendData();
} else if (v.getId() == R.id.clear) {
textView.setText("");
}
}
//申請(qǐng)授權(quán)
private void requestUsbPermission(UsbDevice device) {
PendingIntent mPermissionIntent = PendingIntent.getBroadcast(this, 0, new Intent(ACTION_DEVICE_PERMISSION), 0);
usbManager.requestPermission(device, mPermissionIntent);
}
//開啟線程讀取數(shù)據(jù)
private void startRead() {
if (mUsbDeviceConnection == null && !mUsbDeviceConnection.claimInterface(mUsbInterface, true))
return;
thread = new Thread(new Runnable() {
@Override
public void run() {
while (true) {
byte[] bytes = new byte[inUsbEndpoint.getMaxPacketSize()];
//讀取數(shù)據(jù), 這里注意最后一個(gè)參數(shù)為0時(shí) 為阻塞讀取窍箍,知道讀到數(shù)據(jù)為止
int ret = mUsbDeviceConnection.bulkTransfer(inUsbEndpoint, bytes, bytes.length, 0);
if (ret > 0) {
//這里因?yàn)閽叽a器的maxPacketSize很写摹(我的設(shè)備只有64byte),所以掃碼到的結(jié)果可能分多次讀取椰棘,
//所以這里將讀取到的數(shù)據(jù)丟到Buff里面纺棺,Buff負(fù)責(zé)分割成單條數(shù)據(jù)
buff.append(Arrays.copyOf(bytes, ret));
//分割獲取單條掃碼數(shù)據(jù)
byte[] code = buff.getCode();
if (code != null) {
final String str = new String(code);
runOnUiThread(new Runnable() {
@Override
public void run() {
textView.setText(str);
}
});
}
} else {
Log.d("xxx", "Read empty data");
}
}
}
});
thread.start();
}
//初始化設(shè)備,找到對(duì)應(yīng)的通信接口
private void initCommunication(UsbDevice device) {
int interfaceCount = device.getInterfaceCount();
for (int interfaceIndex = 0; interfaceIndex < interfaceCount; interfaceIndex++) {
UsbInterface usbInterface = device.getInterface(interfaceIndex);
if ((UsbConstants.USB_CLASS_CDC_DATA != usbInterface.getInterfaceClass())
&& (UsbConstants.USB_CLASS_COMM != usbInterface.getInterfaceClass())
&& UsbConstants.USB_CLASS_HID != usbInterface.getInterfaceClass()) {
continue;
}
for (int i = 0; i < usbInterface.getEndpointCount(); i++) {
UsbEndpoint ep = usbInterface.getEndpoint(i);
if (ep.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK) {
if (ep.getDirection() == UsbConstants.USB_DIR_IN) {
inUsbEndpoint = ep;
} else {
outUsbEndpoint = ep;
}
}
}
if (null == inUsbEndpoint) {
inUsbEndpoint = null;
outUsbEndpoint = null;
mUsbInterface = null;
} else {
//連接成功
mUsbInterface = usbInterface;
mUsbDeviceConnection = usbManager.openDevice(device);
initConnection();
break;
}
}
if (mUsbDeviceConnection == null) {
Toast.makeText(this, "連接失敗", Toast.LENGTH_SHORT).show();
}
}
//初始化邪狞,設(shè)置比特率
private void initConnection() {
if (!mUsbDeviceConnection.claimInterface(mUsbInterface, true)) {
return;
}
int ret = mUsbDeviceConnection.controlTransfer(0x21, 0x22, 0x00, 0, null, 0, 0);
setCdcBaudrate(9600);
}
private void setCdcBaudrate(int baudrate) {
byte[] baudByte = new byte[4];
baudByte[0] = (byte) (baudrate & 0x000000FF);
baudByte[1] = (byte) ((baudrate & 0x0000FF00) >> 8);
baudByte[2] = (byte) ((baudrate & 0x00FF0000) >> 16);
baudByte[3] = (byte) ((baudrate & 0xFF000000) >> 24);
int ret = mUsbDeviceConnection.controlTransfer(0x21, 0x20, 0, 0, new byte[]{
baudByte[0], baudByte[1], baudByte[2], baudByte[3], 0x00, 0x00,
0x08
}, 7, 0);
}
//發(fā)送數(shù)據(jù)
private void sendData() {
byte[] buffer = "test data".getBytes();
int ret = mUsbDeviceConnection.controlTransfer(UsbConstants.USB_DIR_OUT, 1, 1, 1, buffer, buffer.length, 1000);
Toast.makeText(this, "send " + ret + " bytes data", Toast.LENGTH_SHORT).show();
}
}
Buff 用于 分割掃碼數(shù)據(jù)祷蝌,我用的設(shè)備是以回車符(0X )+換行符(0X0A)為掃碼結(jié)束分隔符
public class Buff {
private final int DELIMITER_1 = 0xd; //回車符
private final int DELIMITER_2 = 0xa; //換行符
private byte[] buffer;
public void append(byte[] data) {
if (buffer == null) {
buffer = new byte[data.length];
System.arraycopy(data, 0, buffer, 0, data.length);
} else {
byte[] temp = new byte[buffer.length + data.length];
System.arraycopy(buffer, 0, temp, 0, buffer.length);
System.arraycopy(data, 0, temp, buffer.length, data.length);
buffer = temp;
}
}
public byte[] getCode() {
if (buffer == null)
return null;
if (buffer.length > 2) {
boolean found = false;
int i = 0;
for (; i < buffer.length - 1; i++) {
if (buffer[i] == DELIMITER_1 && buffer[i + 1] == DELIMITER_2) {
found = true;
break;
}
}
if (found) {
byte[] result = Arrays.copyOf(buffer, i);
if (i < buffer.length - 2) {
buffer = Arrays.copyOfRange(buffer, i + 2, buffer.length);
} else {
buffer = null;
}
return result;
}
}
return null;
}
}