Android平台开发-Android keypad map-Android按键事件

本文详细解读了Android系统中按键事件的底层处理过程,包括设备打开、映射关系建立以及按键事件分发机制。重点介绍了如何通过 ioctl 命令获取设备名称,以及如何在 KeyLayoutMap 和 KeyCharacterMap 文件中定义按键映射关系,最终通过 InputDispatcherThread 将按键事件分发到相应的窗口。此外,文章还详细阐述了按键事件在Android系统中的处理流程,包括扫描设备、解析映射文件、按键映射以及事件分发等关键步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、Android底层按键事件处理过程
 
   在系统启动后,在文件。。。中,android 会通过
    static const char *device_path = "/dev/input"; 
    bool EventHub::penPlatformInput(void) 
    res = scan_dir(device_path);
    通过下面的函数打开设备。
    int EventHub::pen_device(const char *deviceName) 
    { 
      ... 
      fd = open(deviceName, O_RDWR); 
      ...  
      mFDs[mFDCount].fd = fd; 
      mFDs[mFDCount].events = POLLIN; 
      ... 
      ioctl(mFDs[mFDCount].fd, EVIOCGNAME(sizeof(devname)-1), devname); 
      ... 
      const char* root = getenv("ANDROID_ROOT"); 
      snprintf(keylayoutFilename, sizeof(keylayoutFilename), 
                 "%s/usr/keylayout/%s.kl", root, tmpfn); 
      ... 
      device->layoutMap->load(keylayoutFilename); 
      ... 
  }
  打开设备的时候,如果 device->classes&CLASS_KEYBOARD 不等于 0 表明是键盘。 
  常用输入设备的定义有: 
  enum { 
        CLASS_KEYBOARD      = 0x00000001, //键盘 
        CLASS_ALPHAKEY      = 0x00000002, // 
        CLASS_TOUCHSCREEN   = 0x00000004, //触摸屏 
        CLASS_TRACKBALL     = 0x00000008  //轨迹球 
  };
  打开键盘设备的时候通过上面的 ioctl 获得设备名称,命令字 EVIOCGNAME 的定义在文件:  
  kernel/include/linux/input.h 中。
  对于按键事件,调用mDevices->layoutMap->map进行映射,调用的是文件 KeyLayoutMap.cpp
  (frameworks\base\libs\ui)中的函数:
  status_t KeyLayoutMap::load(const char* filename)通过解析 <Driver name>.kl 把按键的
  映射关系保存在 :KeyedVector<int32_t,Key> m_keys; 中。
  当获得按键事件以后调用: status_t KeyLayoutMap::map(int32_t scancode, int32_t
  *keycode, uint32_t *flags)
  由映射关系 KeyedVector<int32_t,Key> m_keys 把扫描码转换成andorid上层可以识别的按键。

二、按键映射
 
  Key layout maps的路径是 /system/usr/keylayout,第一个查找的名字是按键驱动的名字,例如
  mxckpd.kl。如果没有的话,默认为qwerty.kl。
  Key character maps的路径是 /system/usr/keychars,第一个查找的名字是按键驱动的名字,例如
  mxckpd.kcm。如果没有的话,默认为qwerty.kl。
  
  qwerty.kl是 UTF-8类型的,格式为:key SCANCODE KEYCODE [FLAGS...]。
  
  SCANCODE表示按键扫描码;
  KEYCODE表示键值,例如HOME,BACK,1,2,3...
  FLAGS有如下定义:
    SHIFT: While pressed, the shift key modifier is set 
    ALT: While pressed, the alt key modifier is set 
    CAPS: While pressed, the caps lock key modifier is set 
    WAKE: When this key is pressed while the device is asleep, the device will
                  wake up and the key event gets sent to the app. 
    WAKE_DROPPED: When this key is pressed while the device is asleep, the device
                  will wake up and the key event does not get sent to the app
  
  qwerty.kcm文件为了节省空间,在编译过程中会用工具makekcharmap转化为二进制文件qwerty.bin。
三、按键分发
 
  1、输入事件分发线程
  
    在frameworks/base/services/java/com/android/server/WindowManagerService.java里创
    建了一个输入事件分发线程,它负责把事件分发到相应的窗口上去。
    
    在WindowManagerService类的构造函数WindowManagerService()中:
        mQueue = new KeyQ(); //读取按键
        mInputThread = new InputDispatcherThread();  //创建分发线程     
        ...     
        mInputThread.start();
      
    在启动的线程InputDispatcherThread中:
        run() 
        process(); 
        QueuedEvent ev = mQueue.getEvent(...) 
      
      在process() 方法中进行处理事件:
        switch (ev.classType) 
          case RawInputEvent.CLASS_KEYBOARD: 
             ... 
             dispatchKey((KeyEvent)ev.event, 0, 0); 
             mQueue.recycleEvent(ev); 
             break; 
          case RawInputEvent.CLASS_TOUCHSCREEN: 
             //Log.i(TAG, "Read next event " + ev); 
             dispatchPointer(ev, (MotionEvent)ev.event, 0, 0); 
             break; 
         case RawInputEvent.CLASS_TRACKBALL:
             dispatchTrackball(ev, (MotionEvent)ev.event, 0, 0);
             break;
             
  2、上层读取按键的流程
 
    WindowManagerService()  //(frameworks\base\services\java\com\android\server
                                                    \WindowManagerService.java)
      |
    KeyQ()  //KeyQ 是抽象类 KeyInputQueue 的实现
      |
    InputDeviceReader //在 KeyInputQueue 类中创建的线程 
     |
    readEvent()  //
     |
    android_server_KeyInputQueue_readEvent() //frameworks\base\services\jni\
                                              com_android_server_KeyInputQueue.cpp
     |
    hub->getEvent()
     |
    EventHub::getEvent() //frameworks\base\libs\ui\EventHub.cpp
     |
    res = read(mFDs.fd, &iev, sizeof(iev)); //
<think>首先,用户的问题是关于Android开发中物理按键的:用户想知道KEYCODE_POUND = 18对应的物理按键是哪个键。 我回顾了系统级指令: - 所有行内数学表达式必须使用$...$格式。 - 独立公式必须使用$$...$$格式并单独成段。 - LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 回答中引用的段落末尾自然地添加引用标识。 用户可见层指令: - 回答结构清晰,帮助用户逐步解决问题。 - 保证回答真实可靠。 参考站内引用: - 引用[1]:关于Android keypad map,qwerty.kl文件映射ScanCode到KeyCode。 - 引用[2]:Android物理按键功能更改,kl文件中key后面的数字是kernel上报的按键码,字符标签是Android按键标签。 - 引用[3]:getevent和sendevent工具。 用户的问题具体是:KEYCODE_POUND = 18对应的物理按键是哪个键。 在Android开发中,KEYCODE_POUND是常量,表示"#"键。它的值是18。 我需要解释这个键对应的物理按键。在Android中,物理按键的映射是通过KeyLayout文件(如qwerty.kl)定义的。这些文件将ScanCode(来自硬件)映射到KeyCode(在Android中使用)。 KEYCODE_POUND通常对应于键盘上的"#"键或"pound"键。在标准键盘上,这可能就是数字键盘上的"#"键,或者在某些上下文中是电话键盘上的"pound"键。 从引用中: - 引用[2]提到:kl文件中,key后面的数字是kernel上报的按键码,字符标签是Android按键标签。 - 例如,音量+键对应VOLUME_UP。 所以,对于KEYCODE_POUND,它应该映射到一个具体的物理按键。但物理按键的ScanCode取决于硬件。 用户想知道是哪个物理按键。在标准Android设备上,KEYCODE_POUND通常对应于电话拨号盘上的"#"键。但在物理键盘上,它可能是一个特定的键。 在Android文档中,KEYCODE_POUND定义为"The '#' key.",值18。 在实际设备中,物理按键的识别: - 如引用[1]所说,需要知道键盘矩阵值或通过Log打印。 - 引用[3]提到getevent工具,可以用来查看原始事件。 所以,回答应该: 1. 解释KEYCODE_POUND是什么。 2. 说明它通常对应的物理按键。 3. 建议如何在实际设备上确定映射。 结构清晰: - 逐步解决问题。 - 先介绍KEYCODE_POUND。 - 然后解释映射过程。 - 最后给出方法。 生成相关问题: - 在回答末尾添加相关问题。 使用中文回答。 添加引用: - 在相关段落末尾添加引用标识。 现在,构建回答: 1. **介绍KEYCODE_POUND**: - KEYCODE_POUND是Android中的一个常量,表示"#"键,其值定义为18。在Android开发中,它常用于处理键盘输入事件。 2. **解释物理按键映射**: -Android中,物理按键的映射是通过KeyLayout文件(如qwerty.kl)定义的。这些文件将ScanCode(来自kernel的按键码)映射到KeyCode(如KEYCODE_POUND)。 - 例如,在kl文件中,一行如 `key 18 POUND` 表示ScanCode 18映射到KEYCODE_POUND。 - 但实际的ScanCode取决于硬件设备。不同的设备可能有不同的物理按键布局。 3. **通常对应的物理按键**: - 在大多数设备上,KEYCODE_POUND对应于电话键盘上的"#"键。例如,在拨号盘或某些输入法中。 - 在标准QWERTY键盘上,它可能不是直接映射,但通常数字键盘上的"#"键会触发这个事件。 4. **如何确定特定设备上的映射**: - 如引用[1]所述,如果厂家不提供键盘矩阵,可以通过打印Log来确定。 - 使用adb命令和getevent工具来查看原始ScanCode: - 运行 `adb shell getevent`,然后按下物理按键,查看输出的ScanCode。 - 然后,在kl文件中检查这个ScanCode映射到哪个KeyCode。 - 例如,按下"#"键,getevent会显示ScanCode,然后你可以在kl文件中找到对应的行。 5. **引用添加**: - 在解释映射过程时,添加引用[1]和[2]。 - 在提到getevent时,添加引用[3]。 生成相关问题: - 基于主题,生成3个相关问题。 - 例如: 1. 如何修改Android设备的按键映射? 2. 在Android中如何处理自定义物理按键事件? 3. KEYCODE_POUND在哪些常见应用中使用? 确保LaTeX:用户问题中没有数学表达式,所以可能不需要。但如果有计算,使用格式。这里可能不需要。 最终回答结构: - 开头直接回答问题。 - 然后逐步解释。 - 最后相关问题。</think>### KEYCODE_POUND = 18 对应的物理按键Android开发中,`KEYCODE_POUND` 是一个系统常量,其值定义为18,表示键盘上的"#"键(通常称为"pound"键或"井号键")。这个键码用于处理按键事件,例如在输入法或拨号界面中。下面我将逐步解释它对应的物理按键,以及如何在实际设备上确定映射。 #### 1. **KEYCODE_POUND 的基本定义** - `KEYCODE_POUND` 是Android框架中的一个标准键码,常量值为18。它对应字符"#",常用于电话拨号、密码输入或特殊符号输入场景。 -Android源代码中,它被定义为 `KeyEvent.KEYCODE_POUND`,您可以在代码中直接使用它来监听按键事件,例如: ```java @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_POUND) { // 处理"#"键按下事件 return true; } return super.onKeyDown(keyCode, event); } ``` - 这个键码是抽象的,不直接绑定到特定物理按键;它的具体映射取决于硬件设备和系统配置文件。 #### 2. **物理按键的映射过程** 在Android中,物理按键的识别通过以下步骤完成: - **硬件层**:当用户按下物理按键时,kernel(内核)会生成一个ScanCode(扫描码),这是一个原始硬件编码,代表按键在键盘矩阵中的位置。 - **映射层**:Android使用KeyLayout文件(如`qwerty.kl`)将ScanCode映射到KeyCode(如`KEYCODE_POUND`)。这些文件位于设备的`/system/usr/keylayout/`目录下。 - 例如,在kl文件中,一行配置如 `key 18 POUND` 表示ScanCode 18映射到`KEYCODE_POUND`。 - 但ScanCode的值因设备而异:不同厂商的键盘矩阵可能不同。因此,KEYCODE_POUND = 18 对应的物理按键通常是键盘上的"#"键,但具体位置需参考设备硬件[^1][^2]。 - **常见物理按键**: - 在大多数Android设备上,`KEYCODE_POUND` 对应电话键盘的"#"键(如拨号盘右下角)。 - 在全键盘设备(如外接QWERTY键盘)上,它可能对应数字键盘区的"#"键或主键盘区的Shift+3组合。 - 但这不是绝对的:某些设备可能将"#"键映射到其他键码,或根本没有物理"#"键(需通过屏幕软键盘输入)。 #### 3. **如何确定特定设备上的物理按键** 如果设备厂商未提供键盘映射文档,您可以通过以下方法自行验证: - **使用getevent工具**: - 连接设备到PC,运行ADB命令:`adb shell getevent`。 - 按下物理"#"键,观察输出。例如: ``` /dev/input/eventX: 0001 0012 00000001 # ScanCode为0x12(十进制18) ``` - 这里`0012`是十六进制的ScanCode(18的十六进制是0x12),表明该按键事件对应ScanCode 18。然后,检查kl文件确认它映射到`KEYCODE_POUND`[^3]。 - **查看KeyLayout文件**: - 提取设备的kl文件:`adb pull /system/usr/keylayout/qwerty.kl`。 - 搜索行如 `key 18 POUND`。如果存在,表示ScanCode 18直接映射到"#"键;否则,需检查其他kl文件(如`Generic.kl`)。 - **Logcat日志**: - 在代码中添加日志:`Log.d("KeyTest", "Key pressed: " + keyCode);`。 - 按下按键,如果输出`Key pressed: 18`,则确认该物理键触发`KEYCODE_POUND`[^1]。 **注意事项**: - 如果kl文件中ScanCode 18映射到其他标签(如`CALL`),可能需要修改kl文件来纠正映射[^2]。 - 在模拟器或某些设备上,物理"#"键可能不可用,需使用`sendevent`工具模拟事件[^3]。 #### 总结 `KEYCODE_POUND = 18` 对应的物理按键通常是键盘上的"#"键,但具体取决于设备硬件和kl文件配置。在标准电话或键盘设备上,它位于拨号盘右下角或数字键盘区。要精确确定,请结合`getevent`工具和kl文件分析。如果您提供设备型号,我可以给出更具体的建议。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值