一、 文档概述
本文档主要描述如何对安卓系统中外部键盘输入进行识别,根据输入设备的不同采取不同的操作。
二、 原理说明
系统使用的是CubieBoard2开发板(即树莓派3),运行安卓4.2操作系统。其硬件的结构如下:
按照门禁系统的业务流程,正常情况下通过物理按键输入房号进行呼叫或者开门密码,同时也可以通过刷ID或IC卡直接开门。
在使用LINUX或其他较为底层的嵌入式系统中,根据接口的不同,可以轻易的识别外部输入设备,从而进行业务判断。而本应用场景基于安卓系统,由于良好的封装性,底层输入对于上层应用来说,都是键盘输入,并没有明显的区分输入究竟是物理按键还是RFID读卡器。
不过,在对于安卓系统的一定了解之后,发现底层输入最终通过事件发送给安卓系统,安卓系统对获取到的事件进行处理。然后发现EditText中有这样的一个接口:
public boolean onKey(View v, int keyCode, KeyEvent event);
于是,这里猜想,根据KeyEvent的信息应该可以识别不同的输入设备。
于是重写该接口,将每次获取到的事件都打印出来:
editText = (EditText) findViewById(R.id.et_main_num);
editText.setOnKeyListener(new OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
Log.v("KEYEVENT",event.getDevice().toString());
return false;//返回false表示事件能够被后续的服务所获取;返回true表示该按键输入事件不再分发给后续服务。
}
});
然后分别进行按键输入和RFID刷卡输入,可看到如下信息:
物理按键输入:
V/KeyEvent(8551): Input Device 8: RFID Reader With Keyboard USB RFID Reader
V/KeyEvent(8551): Descriptor: b7e9cbd07a552feaf97d1ae331d71a89ea1007c9
V/KeyEvent(8551): Generation: 28
V/KeyEvent(8551): Location: external
V/KeyEvent(8551): Keyboard Type: alphabetic
V/KeyEvent(8551): Has Vibrator: false
V/KeyEvent(8551): Sources: 0x101 ( keyboard )
RFID刷卡输入:
V/KeyEvent(8551): Input Device 6: sw-keyboard
V/KeyEvent(8551): Descriptor: 485d69228e24f5e46da1598745890b214130dbc4
V/KeyEvent(8551): Generation: 10
V/KeyEvent(8551): Location: built-in
V/KeyEvent(8551): Keyboard Type: non-alphabetic
V/KeyEvent(8551): Has Vibrator: false
V/KeyEvent(8551): Sources: 0x101 ( keyboard )
可以看出,KeyEvent中的信息包含了设备的名称,描述符等信息,在处理时可以根据这些信息区分输入设备。其中Sources字段表明无论哪种输入,均被安卓系统识别为键盘输入。
通过这个实验,发现每次按键输入或者刷卡输入时,KeyEvent都会被打印两次,导致这个的原因是因为onKey()会分别在ACTION_DOWN和ACTION_UP时被触发,ACTION_DOWN和ACTION_UP分别对应按键按下和按键松开动作。
这里需要注意的是,在动作为ACTION_UP的KeyEvent事件之后,按键的值才会显示在对应的EditText上。如果在动作为ACTION_DOWN的KeyEvent事件中,获取EditText的文本,只能获取到不包括当前按下的按键的文本,当前的按键值可以通过keyCode获取。
利用这个特性,当不需要将按键值显示到EditText上时,可以只处理ACTION_DOWN的事件,丢弃ACTION_UP的事件。
可参照如下代码:
cardID=new StringBuffer();
editText = (EditText) findViewById(R.id.et_main_num);
editText.setOnKeyListener(new OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
String editString = ((EditText) v).getText().toString();
//根据输入设备的不同,进行不同的处理,分为ADC键盘、RFID读卡器和其他设备(如鼠标等)
if(event.getDevice().getName().contains("RFID")){//RFID输入主要读取IC卡的ID
editText.setText("");
if(event.getAction()==KeyEvent.ACTION_DOWN){
int tmpKeyCode=event.getKeyCode();
if(tmpKeyCode==KeyEvent.KEYCODE_ENTER){//获取到ENTER字符时,表示ID卡已读取完成。
Log.v("KEYEVENT","Card ID:"+cardID);
cardID.setLength(0);
}else{
//将获取到的KEYCODE添加到字符串,keycode2Char为自定义函数,将KEYCODE转换为char
cardID.append(keycode2Char(tmpKeyCode));
}
//直接返回true,后续的系统监听将得不到这个事件,这里将获取到的数字不在EditText上显示。
return true;
}else if(event.getAction()==KeyEvent.ACTION_UP){
//不处理ACTION_UP事件
return true;
}
}else if (event.getDevice().getSources()==257){//257为键盘输入,ADC键盘或者其他键盘输入
//为了使特定的字符不显示在editText上,在收到ACTION_DOWN事件后进行按键处理,
if(event.getAction()==KeyEvent.ACTION_DOWN){
…//按键处理
return true;
}else if(event.getAction()==KeyEvent.ACTION_UP){
//不处理ACTION_UP事件
return true;
}
}
//除了以上的几种设备之外,其他的设备返回false,避免其他设备无法正常工作
return false;
}
});
需要注意的是,这里的onKey()接口只会在物理输入时进行触发,包括外接的鼠标键盘。而使用系统自带的虚拟键盘输入时并不会触发。