標籤:i/o toolbar not cimage 虛擬鍵盤 asc arc gem listener
外圍輸入裝置,例如:藍芽鍵盤,usb鍵盤,barcode掃碼槍...
由於平時都是在做純軟體程式的開發,博主在需求遇到android裝置與外圍裝置互動時有點不知所措。我最初的思路是這樣:既然是藍芽串連,那不就是socket嗎,那麼截獲他的I/O流然後解析裡面的內容...那不就ok啦?
然而事情並沒有那麼簡單,首先解析資料流是一個痛點,再一個萬一我藍芽串連換成usb串連,或者wifi,那不就得再改了?
參考了網上的方案後發現,外圍裝置是通過KeyEvent事件機制android互動的,既然是這樣那我就不用再關心外設是通過什麼方式串連的,直接截獲外設發送的事件,並且還可直接擷取到輸入內容!
本文將通過一個android裝置與掃碼槍串連案例來向大家詳述android的事件機制首先看一個我具體使用的類庫
以下就是我寫的的藍芽掃碼槍的類庫,已上傳至github
https://github.com/sally519/BarCode-android
我們直接來看看是怎麼用的
public class MainActivity extends AppCompatActivity implements BarCodeIpml.OnScanSuccessListener {//activity實現了BarCodeIpml.OnScanSuccessListener借口,即回調成功時的借口private BarCodeIpml barCodeIpml = new BarCodeIpml();private TextView textView;private TextView mTv;private static final String TAG="MainActivity";@Overrideprotected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //設定掃碼成功的回調監聽 barCodeIpml.setOnGunKeyPressListener(this); mTv = (TextView) findViewById(R.id.mTv);}//重寫事件分發的方法@Overridepublic boolean dispatchKeyEvent(KeyEvent event) { if (barCodeIpml.isEventFromBarCode(event)) { barCodeIpml.analysisKeyEvent(event); return true; } Log.e("keycode",event.getKeyCode()+""); return super.dispatchKeyEvent(event);}//在activity獲得焦點時@Overrideprotected void onResume() { super.onResume(); try { barCodeIpml.hasConnectBarcode(); } catch (DevicePairedNotFoundException e) { e.printStackTrace(); Log.e(TAG, "badcode槍未串連!"); }} //借口的實現,掃碼成功後的回調,寫你自己的實現@Overridepublic void onScanSuccess(String barcode) { Log.e("mcallback", barcode); mTv.setText(barcode);} //與activity生命週期綁定,防止記憶體流失@Overrideprotected void onDestroy() { super.onDestroy(); barCodeIpml.onComplete();}}
從注釋和方法的名稱我們大概可以判斷出各個方法的作用,我們來看其中最關鍵的一個方法
dispatchKeyEvent(KeyEvent event)
大家首先需要知道,這個方法返回一個boolean
@Overridepublic boolean dispatchKeyEvent(KeyEvent event) { if (barCodeIpml.isEventFromBarCode(event)) { barCodeIpml.analysisKeyEvent(event); return true; } Log.e("keycode",event.getKeyCode()+""); return super.dispatchKeyEvent(event);}
當我返回一個true的時候,這個事件就會被我們消費(類庫裡的主要操作就是擷取KeyCode,即外圍裝置輸入的內容),不會再交給系統處理。在以上的代碼中我拿到這個事件後我調用類庫的方法判斷他是否來自外圍裝置,如果是的話我們自己將其截獲處理,不再交給系統,否則的話我們交由系統處理。這個方法大家應該比較熟悉,我們常常用來重寫back鍵或者home鍵。
之後我們進去dispatchKeyEvent裡面看一下,系統是如何處理這個事件的,直接在activity中查看源碼
/** * Called to process key events. You can override this to intercept all * key events before they are dispatched to the window. Be sure to call * this implementation for key events that should be handled normally. * * @param event The key event. * * @return boolean Return true if this event was consumed. */public boolean dispatchKeyEvent(KeyEvent event) { onUserInteraction(); // Let action bars open menus in response to the menu key prioritized over // the window handling it final int keyCode = event.getKeyCode(); if (keyCode == KeyEvent.KEYCODE_MENU && mActionBar != null && mActionBar.onMenuKeyEvent(event)) { return true; } else if (event.isCtrlPressed() && event.getUnicodeChar(event.getMetaState() & ~KeyEvent.META_CTRL_MASK) == ‘<‘) { // Capture the Control-< and send focus to the ActionBar final int action = event.getAction(); if (action == KeyEvent.ACTION_DOWN) { final ActionBar actionBar = getActionBar(); if (actionBar != null && actionBar.isShowing() && actionBar.requestFocus()) { mEatKeyUpEvent = true; return true; } } else if (action == KeyEvent.ACTION_UP && mEatKeyUpEvent) { mEatKeyUpEvent = false; return true; } }
源碼不算複雜,注釋說你可以在事件發送到視窗前攔截此事件,但要對關鍵事件執行,這裡說的關鍵事件可能是back鍵或者home鍵。
我們一步一步來做下分析,先來看看onUserInteraction()
/** * Called whenever a key, touch, or trackball event is dispatched to the * activity. Implement this method if you wish to know that the user has * interacted with the device in some way while your activity is running. * This callback and {@link #onUserLeaveHint} are intended to help * activities manage status bar notifications intelligently; specifically, * for helping activities determine the proper time to cancel a notfication. * * <p>All calls to your activity‘s {@link #onUserLeaveHint} callback will * be accompanied by calls to {@link #onUserInteraction}. This * ensures that your activity will be told of relevant user activity such * as pulling down the notification pane and touching an item there. * * <p>Note that this callback will be invoked for the touch down action * that begins a touch gesture, but may not be invoked for the touch-moved * and touch-up actions that follow. * * @see #onUserLeaveHint() */ public void onUserInteraction() {}
實現居然是空的,注釋上說當事件分發到activity的時候調用,你可以實現這個方法來獲知使用者是否在和當前activity互動。onUserInteraction()和onUserLeaveHint()是本意是協助activity更好的管理推送訊息,後者將會跟隨前者一起調用,上面還說onUserInteraction()可能不會跟隨著手指的移動而調用。
由此,我們也知道onUserInteraction()會在事件分發最初的時候調用,我們可以用這和方法監聽使用者於activity的互動。
我們接著往下看
// Let action bars open menus in response to the menu key prioritized over// the window handling itfinal int keyCode = event.getKeyCode();if (keyCode == KeyEvent.KEYCODE_MENU && mActionBar != null && mActionBar.onMenuKeyEvent(event)) { return true;}
首先擷取使用者的輸入內容keyCode ,之後的意圖很明顯,如果使用者點擊的是ToolBar或者ActionBar的菜單那直接return了一個true,我們剛剛說過return一個true的意思就是事件不再交給系統處理,return一個false則依舊需要交給系統處理,這裡的目的想必就是把我們的事件拋給ToolBar或者ActionBar來處理。
接著往下看
else if (event.isCtrlPressed() && event.getUnicodeChar(event.getMetaState() & ~KeyEvent.META_CTRL_MASK) == ‘<‘) { // Capture the Control-< and send focus to the ActionBar final int action = event.getAction(); if (action == KeyEvent.ACTION_DOWN) { final ActionBar actionBar = getActionBar(); if (actionBar != null && actionBar.isShowing() && actionBar.requestFocus()) { mEatKeyUpEvent = true; return true; } } else if (action == KeyEvent.ACTION_UP && mEatKeyUpEvent) { mEatKeyUpEvent = false; return true; }
第一個判斷條件event.isCtrlPressed() &&event.getUnicodeChar(event.getMetaState() & ~KeyEvent.META_CTRL_MASK) == ‘<‘或許有點抽象,但下面的注釋告訴我們這段代碼的意圖是捕獲“<”鍵,之後判斷ActionBar是否在請求焦點,如果是的話強行將ACTION_UP的事件(也就是按壓螢幕後抬起那一下)消費,不再由系統處理,並且強行讓ActionBar獲得焦點,交由ActionBar處理。否則告訴activityACTION_UP的事件還未被消費。
搞懂了以上的內容我們就可以隨意截獲事件,並通過KeyCode獲知是否點擊了虛擬鍵盤,具體點擊的是哪一個鍵?如果是斷行符號鍵,那就將其內容通過回調發送給activity,由此來擷取外圍輸入裝置的內容。具體可查看 https://github.com/sally519/BarCode-android 中的使用詳情。
#小結
好了,我們來總結幾個需要特別注意的點
- dispatchKeyEvent(KeyEvent event)中,但我們返回了true,則事件將會被消費,不會在有系統處理,當返回false則還要交由系統來處理
- onUserInteraction()和onUserLeaveHint()可以用來監聽使用者的和activity的互動,注意:activity必須是獲得焦點的
- 我們在消費事件的時候需要對注意home鍵back鍵等常用的鍵,如果沒有特殊需求,記得將他們拋給系統處理
博主難免有疏忽和遺漏,若有錯誤或疑點歡迎指正!以上內容均為原創,歡迎關注博主!
巧用事件分發機制,和我一起hold住android外圍裝置