前言
公司的設(shè)備以前接入的都是串口的掃碼頭,優(yōu)點是直接通過串口讀取流里面的數(shù)據(jù)就OK了,缺點是你需要知道每一款掃碼器的型號以獲取波特率及Android設(shè)備的串口地址。因為現(xiàn)在usb掃碼器越來越方便且即插即用,不需要額外供電以及價格便宜等特點,公司以后開發(fā)的設(shè)備都打算采用usb掃碼器。所以我開始嘗試接入usb掃碼器,下面就是我在接入時的方法以及遇到的一些問題。
1. USB掃碼器接入
- 前面我有說過,usb掃碼器接入方便,即插即用,但是有個很大的坑,因為它的實質(zhì)其實就是相當(dāng)于設(shè)備的外接鍵盤,也就是它必須在有光標(biāo)的地方才能進(jìn)行掃碼,且是直接把掃到的內(nèi)容自動輸入到輸入框中,并不受我們的控制。但是我們在很多時候并不需要一個edittext的輸入框的,而這時要么就是重新改設(shè)計,要么就是我們自己想辦法解決這個問題。
- 最開始的時候我是利用1個寬高都為1px的Edittext作為掃碼頭的接收器,并且讓它自動獲取焦點,這樣我們就能實時地獲取到掃碼頭傳過來的數(shù)據(jù)了。但是這種方法并不能作為一個通用的方法,且每個項目都不能復(fù)用,還會和我們頁面中其它Edittext輸入框沖突,這個方法也就被我棄用了。
- 接下來說到的就是我現(xiàn)在用到的方法,我們知道,USB掃碼器實質(zhì)就是一個外接鍵盤,那么我們可不可以對它進(jìn)行鍵盤的輸入攔截呢,安卓系統(tǒng)中有這么個方法
dispatchKeyEvent(KeyEvent event),它就是用來處理我們鍵盤的輸入事件的,如果我們攔截該方法,把它交給我們自己去處理,這樣我們就可以不通過Edittext從而獲取到掃碼頭傳過來的數(shù)據(jù)了。 - 我自定義了一個叫ScanKeyManager的攔截鍵盤事件并將它轉(zhuǎn)化成我們需要的數(shù)據(jù)的管理類代碼如下:
import android.view.KeyEvent;
public class ScanKeyManager {
private StringBuilder mResult;
public OnScanValueListener mListener;
private boolean mCaps;
public interface OnScanValueListener {
void onScanValue(String value);
}
public ScanKeyManager(OnScanValueListener listener) {
this.mListener = listener;
this.mResult = new StringBuilder();
}
/**
* 掃碼設(shè)備事件解析
*/
public void analysisKeyEvent(KeyEvent event) {
int keyCode = event.getKeyCode();
checkLetterStatus(event);
if (event.getAction() == KeyEvent.ACTION_DOWN) {
char aChar = getInputCode(mCaps, event.getKeyCode());
if (aChar != 0) {
mResult.append(aChar);
}
if (keyCode == KeyEvent.KEYCODE_ENTER) {
if (mListener != null) {
mListener.onScanValue(mResult.toString());
}
mResult.delete(0, mResult.length());
}
}
}
/**
* 判斷大小寫
*/
private void checkLetterStatus(KeyEvent event) {
int keyCode = event.getKeyCode();
if (keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT || keyCode == KeyEvent.KEYCODE_SHIFT_LEFT) {
mCaps = event.getAction() == KeyEvent.ACTION_DOWN;
}
}
/**
* 將keyCode轉(zhuǎn)為char
*
* @param caps 是不是大寫
* @param keyCode 按鍵
* @return 按鍵對應(yīng)的char
*/
private char getInputCode(boolean caps, int keyCode) {
if (keyCode >= KeyEvent.KEYCODE_A && keyCode <= KeyEvent.KEYCODE_Z) {
return (char) ((caps ? 'A' : 'a') + keyCode - KeyEvent.KEYCODE_A);
} else {
return keyValue(caps, keyCode);
}
}
/**
* 按鍵對應(yīng)的char表
*/
private char keyValue(boolean caps, int keyCode) {
switch (keyCode) {
case KeyEvent.KEYCODE_0:
return caps ? ')' : '0';
case KeyEvent.KEYCODE_1:
return caps ? '!' : '1';
case KeyEvent.KEYCODE_2:
return caps ? '@' : '2';
case KeyEvent.KEYCODE_3:
return caps ? '#' : '3';
case KeyEvent.KEYCODE_4:
return caps ? '$' : '4';
case KeyEvent.KEYCODE_5:
return caps ? '%' : '5';
case KeyEvent.KEYCODE_6:
return caps ? '^' : '6';
case KeyEvent.KEYCODE_7:
return caps ? '&' : '7';
case KeyEvent.KEYCODE_8:
return caps ? '*' : '8';
case KeyEvent.KEYCODE_9:
return caps ? '(' : '9';
case KeyEvent.KEYCODE_NUMPAD_SUBTRACT:
return '-';
case KeyEvent.KEYCODE_MINUS:
return '_';
case KeyEvent.KEYCODE_EQUALS:
return '=';
case KeyEvent.KEYCODE_NUMPAD_ADD:
return '+';
case KeyEvent.KEYCODE_GRAVE:
return caps ? '~' : '`';
case KeyEvent.KEYCODE_BACKSLASH:
return caps ? '|' : '\\';
case KeyEvent.KEYCODE_LEFT_BRACKET:
return caps ? '{' : '[';
case KeyEvent.KEYCODE_RIGHT_BRACKET:
return caps ? '}' : ']';
case KeyEvent.KEYCODE_SEMICOLON:
return caps ? ':' : ';';
case KeyEvent.KEYCODE_APOSTROPHE:
return caps ? '"' : '\'';
case KeyEvent.KEYCODE_COMMA:
return caps ? '<' : ',';
case KeyEvent.KEYCODE_PERIOD:
return caps ? '>' : '.';
case KeyEvent.KEYCODE_SLASH:
return caps ? '?' : '/';
default:
return 0;
}
}
}
在該類中,我攔截處理了鍵盤輸入事件的絕大部分字符,并把它轉(zhuǎn)化成我們需要的數(shù)據(jù)通過接口回調(diào)傳給我們需要用到的頁面。
- 用法如下
- 在activity中使用
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.KeyEvent;
public class MainActivity extends AppCompatActivity {
private ScanKeyManager scanKeyManager;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//攔截掃碼器回調(diào),獲取掃碼內(nèi)容
scanKeyManager = new ScanKeyManager(new ScanKeyManager.OnScanValueListener() {
@Override
public void onScanValue(String value) {
Log.e("ScanValue", value);
}
});
}
/*監(jiān)聽鍵盤事件,除了返回事件都將它攔截,使用我們自定義的攔截器處理該事件*/
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
if (event.getKeyCode() != KeyEvent.KEYCODE_BACK) {
scanKeyManager.analysisKeyEvent(event);
return true;
}
return super.dispatchKeyEvent(event);
}
}
- 在dialog中使用
public class ScanDialog extends Dialog {
private ScanKeyManager scanKeyManager;
private OnScanDialogListener mListener;
public ScanDialog(Context context) {
super(context, R.style.LoadDialogStyle);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setContentView(R.layout.dialog_scan);
setCanceledOnTouchOutside(false);
scanKeyManager = new ScanKeyManager(new ScanKeyManager.OnScanValueListener() {
@Override
public void onScanValue(String value) {
Log.e("dialog", value);
if (mListener == null) return;
mListener.onScanResult(value);
}
});
}
@Override
public boolean dispatchKeyEvent(@NonNull KeyEvent event) {
scanKeyManager.analysisKeyEvent(event);
return true;
}
public interface OnScanDialogListener {
void onScanResult(String scanValue);
}
public void setOnScanDialogListener(OnScanDialogListener listener) {
mListener = listener;
}
}
使用起來都是比較簡單,只需要創(chuàng)建鍵盤攔截管理類,并設(shè)置監(jiān)聽回調(diào),以及復(fù)寫dispatchKeyEvent(KeyEvent event)攔截鍵盤事件就ok了,如果是dialog中使用則多了一個接口回調(diào)的步驟。
2. 遇到的問題
如果按照上述的方法去做是可以做到完美監(jiān)聽掃碼器掃碼事件的,但是我在實際使用中還遇到了一個比較嚴(yán)重問題,就是如果頁面中有Edittext輸入框的時候,如果我使用該方法攔截鍵盤事件之后,會出現(xiàn)數(shù)字,一些符號,以及刪除鍵等一些字符輸入無效的問題,好像這樣我們在使用鍵盤攔截的時候就沒法使用鍵盤輸入了,那該怎么辦呢?
3. 解決辦法
上面我們說到,使用鍵盤攔截的時候就沒法使用鍵盤輸入了,那么有沒有一種可能,就是我在鍵盤輸入的時候不攔截鍵盤事件,不輸入的時候我才進(jìn)行攔截呢?這個時候就需要監(jiān)聽軟鍵盤的顯示和隱藏事件了,但是安卓系統(tǒng)所提供的api當(dāng)中是沒有監(jiān)聽鍵盤事件的,那么如果需要監(jiān)聽鍵盤事件就需要我們自定義了,我的另一篇文章中就對鍵盤的顯示隱藏事件監(jiān)聽有比較好的解決辦法,有興趣的同學(xué)可以去看看Android軟鍵盤顯示隱藏事件監(jiān)聽。下面就開始上代碼:
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.KeyEvent;
public class SecondActivity extends AppCompatActivity {
private ScanKeyManager scanKeyManager;
/*是否是輸入狀態(tài)(輸入時掃碼監(jiān)聽不攔截)*/
private boolean isInput = false;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
onKeyBoardListener();
//攔截掃碼器回調(diào), 獲取掃碼內(nèi)容
scanKeyManager = new ScanKeyManager(new ScanKeyManager.OnScanValueListener() {
@Override
public void onScanValue(String value) {
Log.e("ScanValue", value);
}
});
}
/*監(jiān)聽鍵盤事件,除了返回事件都將它攔截,使用我們自定義的攔截器處理該事件*/
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
if (event.getKeyCode() != KeyEvent.KEYCODE_BACK && !isInput) {
scanKeyManager.analysisKeyEvent(event);
return true;
}
return super.dispatchKeyEvent(event);
}
//監(jiān)聽軟件盤是否彈起
private void onKeyBoardListener() {
SoftKeyBoardListener.setListener(this, new SoftKeyBoardListener.OnSoftKeyBoardChangeListener() {
@Override
public void keyBoardShow(int height) {
Log.e("軟鍵盤", "鍵盤顯示 高度" + height);
isInput = true;
}
@Override
public void keyBoardHide(int height) {
Log.e("軟鍵盤", "鍵盤隱藏 高度" + height);
isInput = false;
}
});
}
}
那么,這個時候就可以完美解決usb掃碼器的監(jiān)聽問題啦,可能還會有小伙伴會問,那我在鍵盤彈起的時候豈不是就不能用掃碼頭了?這個其實不用擔(dān)心,當(dāng)我們不攔截鍵盤事件的時候,且Edittext獲取焦點時,那么掃到的內(nèi)容就會被自動輸入到輸入框中了。
總結(jié)
這是我摸索出來的算是比較完美解決usb掃碼器使用的辦法了,當(dāng)然如果哪位同學(xué)有更好的解決辦法,也歡迎你在下方留言。如果文章中哪里有錯誤也希望大家多多指正!?(????????)????