Android的keycode添加需要在多个文件中进行键值增加,一个同样的键值在多个地方进行定义总是让人感觉不安的,所以下面我们通过走读代码重点看一下keycode对应关系,上层的keycode值如何对应到/dev/input/eventX中的上报值,为什么要在这些文件中定义,以及各个文件中的定义分别是做什么用的,到底以哪个为准。
系统中的keycode相关的定义的地方有(本文都以DPAD_CENTER为例,其他键值流程是一样的):
key 0x4C DPAD_CENTER
DEFINE_KEYCODE(DPAD_CENTER),
AKEYCODE_DPAD_CENTER = 23,
public static final int KEYCODE_DPAD_CENTER = 23;
<enum name="KEYCODE_DPAD_CENTER" value="23" />
kl中定义了设备/dev/input/eventX中的上报值与系统键值的对应关系
key 0x4C DPAD_CENTER
像这样的格式,即将设备上报的0x4C与DPAD_CENTER关联,过程是通过KeyLayoutMap.cpp的解析代码来实现的。
status_t KeyLayoutMap::Parser::parseKey() {
...
//解析kl文件中event事件上报数值并赋值code,如0x4C
int32_t code = int32_t(strtol(codeToken.string(), &end, 0));
...
KeyedVector<int32_t, Key>& map = mapUsage ? mMap->mKeysByUsageCode : mMap->mKeysByScanCode;
...
mTokenizer->skipDelimiters(WHITESPACE);
//解析kl文件中的键值,如DPAD_CENTER
String8 keyCodeToken = mTokenizer->nextToken(WHITESPACE);
//通过DPAD_CENTER来获取系统中定义的键值
int32_t keyCode = getKeyCodeByLabel(keyCodeToken.string());
...
//保存到map
Key key;
key.keyCode = keyCode;
key.flags = flags;
map.add(code, key);
return NO_ERROR;
}
前面提到了getKeyCodeByLabel方法,用于获取DPAD_CENTER在系统中定义的键值。
static inline int32_t getKeyCodeByLabel(const char* label) {
return int32_t(lookupValueByLabel(label, KEYCODES));
}
static int lookupValueByLabel(const char* literal, const InputEventLabel *list) {
while (list->literal) {
if (strcmp(literal, list->literal) == 0) {
return list->value;
}
list++;
}
return list->value;
}
可以看到getKeyCodeByLabel方法是通过KEYCODES来拿到键值数据的。
#define DEFINE_KEYCODE(key) { #key, AKEYCODE_##key }
struct InputEventLabel {
const char *literal;
int value;
};
static const InputEventLabel KEYCODES[] = {
...
DEFINE_KEYCODE(DPAD_CENTER),
...
}
在InputEventLabels.h中KEYCODES是一个InputEventLabel 的数组,保存了所有的键值。通过DEFINE_KEYCODE宏,这里实际上可以理解为在KEYCODES中添加了
{DPAD_CENTER,AKEYCODE_DPAD_CENTER}。
这里DPAD_CENTER是一个字符串,AKEYCODE_DPAD_CENTER是一个int。getKeyCodeByLabel便能够通过遍历KEYCODES来拿到DPAD_CENTER对应的键值,也就是AKEYCODE_DPAD_CENTER。
键值数字的定义是在keycodes.h中,可以看到AKEYCODE_DPAD_CENTER的值是23。
/** Directional Pad Center key.
* May also be synthesized from trackball motions. */
AKEYCODE_DPAD_CENTER = 23,
keycodes.h中是定义了键值数据的,不过是在C++层。在Java层中也想要方便地使用keycode,因此在KeyEvent.java中也有一份键值的常量定义。
在KeyEvent.java中的键值常量是以KEYCODE_开头的。
/** Key code constant: Directional Pad Center key.
* May also be synthesized from trackball motions. */
public static final int KEYCODE_DPAD_CENTER = 23;
KeyEvent.java中有常用的keyCodeToString和keyCodeFromString方法
public static String keyCodeToString(int keyCode) {
String symbolicName = nativeKeyCodeToString(keyCode);
return symbolicName != null ? LABEL_PREFIX + symbolicName : Integer.toString(keyCode);
}
keyCodeToString即是调用nativeKeyCodeToString来从InputEventLabels.h的KEYCODES中拿到对应的键值字符串,然后在前面加上"KEYCODE_"。
public static int keyCodeFromString(@NonNull String symbolicName) {
try {
int keyCode = Integer.parseInt(symbolicName);
if (keyCodeIsValid(keyCode)) {
return keyCode;
}
} catch (NumberFormatException ex) {
}
if (symbolicName.startsWith(LABEL_PREFIX)) {
symbolicName = symbolicName.substring(LABEL_PREFIX.length());
}
int keyCode = nativeKeyCodeFromString(symbolicName);
if (keyCodeIsValid(keyCode)) {
return keyCode;
}
return KEYCODE_UNKNOWN;
}
keyCodeFromString也是类似,将KEYCODE_DPAD_CENTER去除前缀,然后从 KEYCODES中拿到对应键值。所以看到,键值的真正定义是在InputEventLabels.h和keycodes.h中的。
C层和Java层中都有了键值的常量定义,那attrs.xml的键值又是干什么的呢?
<attr name="keycode">
<enum name="KEYCODE_UNKNOWN" value="0" />
<enum name="KEYCODE_SOFT_LEFT" value="1" />
<enum name="KEYCODE_SOFT_RIGHT" value="2" />
<enum name="KEYCODE_HOME" value="3" />
...
attr>
attrs.xml的键值是为了在XML 布局文件中为相应的自定义 View 设置 keycode 属性
比如以下用法
<com.example.CustomView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:keycode="KEYCODE_SOFT_LEFT" />
public class CustomButton extends Button {
private int keyCode;
public CustomButton(Context context, AttributeSet attrs) {
super(context, attrs);
// 从 XML 属性中获取 keycode 的值
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CustomButton);
keyCode = typedArray.getInt(R.styleable.CustomButton_keycode, 0);
typedArray.recycle();
// 设置点击事件
setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// 根据 keycode 的值执行不同的操作
switch (keyCode) {
case KeyEvent.KEYCODE_SOFT_LEFT:
// 执行 KEYCODE_SOFT_LEFT 对应的操作
break;
// 可以添加更多的 case 来处理其他键码
default:
// 默认操作
break;
}
}
});
}
}
不过目前在Android源码中搜索,使用这个keycode attr的基本没有。
由以上可以看出