Android的用户输入系统,自下而上,分成如下部分:
1.驱动程序:/dev/input目录。负责report键值到上层。其中键值定义在input.h中;
2.根文件系统中,KeyLayout(按键布局)和KeyCharacterMap(按键字符映射):后缀名称分别为kl和kcm;如果使用usb全键盘,则使用/system/usr/keylayout/qwerty.kl.
3.EventHub: libui的一部分,实现了对驱动程序的控制。目录 android/frameworks/base/libs/ui/,读取RawEvent事件。
4.Java框架层的处理:有KeyInputDevice等类来处理EventHub传递上来的信息,这些信息通过RawInputEvent和KeyEvent来表示。
一般情况下,对于按键事件,以后者的形式传送给应用程序,而触摸屏和轨迹球事件以前者的形式转换形成MotionEvent事件传送给应用程序;
5.Android应用程序层:通过重载onKeyDown()和onkeyUp()等方法接收KeyEvent(按键事件),通过重载onTouchEvent()和onTrackballEvent()等方法接收MotionEvent(运动事件);
keyEvent的处理流程:
1、生成
存在这样一个线程,它不断地从driver读取Event,并把它放到RawEvent队列中。这个队列中的RawEvent既有按键,也有触摸、轨迹球等事件。
RawEvent队列中的每个RawEvent最后都会通过一系列转化,最终变为KeyEvent被发送给另外一个线程,即输入线程,也就是一个Activity的主线程。
2、传递
KeyEvent传递过程主要可以划分为三步:过滤器、View树、Activity
过滤器部分
主要对应着PhoneWindowManager.java中的interceptKeyTq和interceptKeyTi这两个方法。
它们的代码可以在frameworks/base/policy/base/phone/com/android/internal/policy/impl/PhoneWindowManager.java中看到。
这两个过滤器最大的不同就是interceptKeyTq用于RawEvent,而interceptKeyTi用于KeyEvent。
在一个没有实体键盘的机器上,Power键会被interceptKeyTq这个过滤器吃掉用来调用关机对话框或者使机器休眠。而Home键会被interceptKeyTi这个过滤器吃掉,用来把当前Activity切换到后台并把桌面程序切换到前台。所以,应用程序在View和Activity的onKeyDown/Up中是监听不到这两个按键的。除了这两个键以外的按键,都有机会继续前进。接下来,KeyEvent会先经过interceptKeyTi过滤器。
如果这个过滤器不吃掉的话,就会继续前进,进入View树。如果没有被哪个View吃掉的话,最后进入到Activity的onKeyDown/Up方法中。
当一个KeyEvent经过上面的过程还没有被吃掉的话,系统就会利用它做一些定制的功能。比如音量键被系统用来调整声音,多媒体按键用来控制媒体播放,搜索键用来快速打开搜索功能,返回键用来退出当前Activity等。
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
如果你的键盘是全键盘(包括了ALT、SHIFT、CAPS_LOCK功能键),基本上用Android默认的键盘映射文件qwerty.kcm,qwerty.kl就可以了。
如果你需要添加新的按键,或者需要修改按键的映射,就需要修改这两个文件了。
添加新的键盘
比如支持usb键盘,需要导入keylayout.
file: frameworks/base/libs/ui/EventHub.cpp
int EventHub::open_device(const char *deviceName)
{
//.................
// find the .kl file we need for this device
const char* root = getenv("ANDROID_ROOT");
snprintf(keylayoutFilename, sizeof(keylayoutFilename),
"%s/usr/keylayout/%s.kl", root, tmpfn);
bool defaultKeymap = false;
if (access(keylayoutFilename, R_OK)) {
snprintf(keylayoutFilename, sizeof(keylayoutFilename),
"%s/usr/keylayout/%s", root, "qwerty.kl");
defaultKeymap = true;
}
device->layoutMap->load(keylayoutFilename);
}
*********问题:这里在导入keylayout文件的时候,是指定路径,通过后缀获取的,所以kl文件名字不重要???
比如使用usb键盘,添加一个按键,物理按键是F1,对应android上的功能是SEARCH:
1 按下物理按键后,驱动通过接口input_report_key向上层input子系统report一个按键SCANCODE:#define KEY_F159 (定义在内核input.h中)
2 查找static const KeycodeLabel KEYCODES[](在文件frameworks/base/include/ui/KeycodeLabels.h中),得知我们需要的功能键的KEYCODE是“SEARCH”
3 在文件/system/usr/keylayout/qwerty.kl中添加下面一行,
key 59 SEARCH
重新启动即可:
总结:android按键消息通过二次转换:
1 将驱动report上来的SCANCODE,通过文件qwerty.kl的映射,得到KEYCODE字串;
2 通过二维表static const KeycodeLabel KEYCODES[],将上面的字符串转换成android 需要的键值信息。
如果是输入键,还需要查询keymap,得到相应的字符。
相关文件:
key scancode定义:
文件:include/linux/input.h
......
#define KEY_SPACE 57
#define KEY_CAPSLOCK 58
#define KEY_F1 59
#define KEY_F2 60
#define KEY_F3 61
#define KEY_F4 62
#define KEY_F5 63
#define KEY_F6 64
#define KEY_F7 65
#define KEY_F8 66
#define KEY_F9 67
#define KEY_F10 68
........
KEYLAYOUT 按键布局
文件:/system/usr/keylayout/qwerty.kl
文件格式:
key SCANCODE KEYCODE [FLAGS...]
第一列:key
第二列: SCANCODE是一个整数,是驱动里面定义的,在文件
第三列: KEYCODE 是一个字串,定义在你描述的布局文件frameworks/base/include/ui/KeycodeLabels.h
另外可以设置相关的FLAGS:
SHIFT: 当按下,自动加上SHIFT键值
ALT:当按下,自动加上ALT
CAPS:当按下,自动带上CAPS大写
WAKE:当按下,当设备进入睡眠的时候,按下这个键将唤醒,而且发送消息给应用层。
WAKE_DROPPED:当按下,且设备正处于睡眠,设备被唤醒,但是不发送消息给应用层。
关于SCANCODE,也无需查找驱动代码。在字符界面下可以使用命令getevent获取按键的scan code:
# getevent
/ $ getevent
add device 1: /dev/input/event4
name: "Fake Touchscreen"
add device 2: /dev/input/event0
name: "gpio-keys"
add device 3: /dev/input/event3
name: "Dell Dell USB Keyboard"
add device 4: /dev/input/event2
name: "PixArt USB Optical Mouse"
add device 5: /dev/input/event1
name: "twl4030_pwrbutton"
支持5种输入设备:触摸屏 GPIOkey usb键盘 usb鼠标 powerkey
点击usb键盘的按键“A”的打印信息:
/dev/input/event3: 0004 0004 00070004
/dev/input/event3: 0001 001e 00000001
/dev/input/event3: 0000 0000 00000000
/dev/input/event3: 0004 0004 00070004
/dev/input/event3: 0001 001e 00000000
/dev/input/event3: 0000 0000 00000000
按下一个GPIO按键的打印信息:
/dev/input/event0: 0001 0114 00000001
/dev/input/event0: 0000 0000 00000000
/dev/input/event0: 0001 0114 00000000
/dev/input/event0: 0000 0000 00000000
其中:
0001实际上是输入设备的类型。(USB键盘上的 0004 0004 00070004不太清楚是什么信息,大概是这个USB设备的各种ID信息吧)
0114是按键的SCANCODE,
00000001和00000000分别是按下和抬起的附加信息。
此文件部分内容如下:
.....
key 30 A
key 31 S
key 32 D
key 33 F
key 34 G
key 35 H
key 36 J
key 79 1
key 80 2
key 81 3
key 96 ENTER
key 69 NUM
key 56 ALT_LEFT
key 100 ALT_RIGHT
key 1 POWER
key 58 NUM
key 111 MUTE
key 1 BACK
key 59 SEARCH
.....
按键码:KEYCODE
frameworks/base/include/ui/KeycodeLabels.h
struct KeycodeLabel {
const char *literal; // KEYCODE
int value; //用于在keymap索引字符的value
};
来源:http://hi.baidu.com/left99/blog/item/561a851cfdaa319786d6b6f3.html