Android外接USB掃碼槍

前言

公司的設(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)傳給我們需要用到的頁面。

  • 用法如下
  1. 在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);
    }
}
  1. 在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é)有更好的解決辦法,也歡迎你在下方留言。如果文章中哪里有錯誤也希望大家多多指正!?(????????)????

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容