# 物理键盘输入异常分析

#### 问题现象

应用在不同设备上使用时，部分依赖物理键盘的交互方式会出现输入异常，具体表现为物理键盘无法正常输入内容，导致用户无法通过键盘进行交互操作，影响用户体验和应用功能的正常运行。

#### 背景知识

*   键盘：键盘是一种重要的生产力输入设备，优秀的键盘使用体验应允许用户快速准确输入文本，双手无需离开键盘即可在系统和应用内使用导航、访问所有的功能以及保证无障碍体验，用户通常使用键盘快捷键一步直达某些功能。
*   焦点导航：当用户使用键盘、电视遥控器、车机摇杆/旋钮等非指向性输入设备与应用程序进行间接交互时，基于焦点的导航和交互是重要的输入手段。
*   焦点事件：焦点事件指页面焦点在可获焦组件间移动时触发的事件，组件可使用焦点事件来处理相关逻辑。焦点开发及组件获焦能力参考支持焦点处理。
*   Input Kit（多模输入服务）：为多种输入设备提供服务，如触控板、触摸屏、鼠标、键盘等。通过对这些输入设备上报驱动事件的归一化处理，确保不同输入设备与用户交互体验统一和流畅。相关API请参考ArkTS API。
    1.  运作机制：多模输入能力作为系统为应用提供的一种基础服务，通过处理上报的输入设备驱动事件，完成输入事件管理、接收、预处理、分发，通过inner SDK与JSkit上报应用，具体运行机制如下。
        
    2.  输入设备开发指导：当用户需要输入文本时，输入法会根据当前是否插入了物理键盘来决定是否弹出虚拟键盘，开发者可以通过监听设备热插拔判断是否有物理键盘插入。
        
        事件拦截开发指导（C/C++）：多模为应用提供了创建和删除按键、输入事件（鼠标、触摸和轴事件）拦截的能力。使用场景例如：云桌面应用需要拦截按键、鼠标、触摸和轴事件。
        
*   支持键盘输入事件：物理按键产生的按键事件为非指向性事件，与触摸等指向性事件不同，其事件并没有坐标位置信息，因此会按照一定次序向用户操作的焦点进行派发，一些文字输入场景，按键事件都会优先派发给输入法软键盘进行处理，以便其处理文字的联想和候选词，应用可以通过onKeyPreIme提前感知事件。
    1.  按键事件：按键事件是指组件与键盘、遥控器等按键设备交互时触发的事件，适用于所有可获焦组件。对于默认不可获焦的组件，可以将focusable属性设置为true后使用按键事件。
    2.  按键事件数据流：按键事件由外设键盘等设备触发，经驱动和多模处理转换后发送给当前获焦的窗口，窗口获取到事件后，会尝试分发三次事件。三次分发的优先顺序如下，一旦事件被消费，则跳过后续分发流程。
        
        1.  首先分发给ArkUI框架用于触发获焦组件绑定的onKeyPreIme回调和页面快捷键。
        2.  再向输入法分发，输入法会消费按键用作输入。
        3.  再次将事件发给ArkUI框架，用于响应onKeyEventDispatch事件、获焦组件绑定的onKeyEvent回调以及走焦。
        
        当某输入框组件获焦，且打开了输入法，此时大部分按键事件均会被输入法消费。
        
        按键事件到ArkUI框架之后，会先找到完整的父子节点获焦链。从叶子节点到根节点，逐一发送按键事件。
        
        Web组件的KeyEvent流程与上述过程有所不同。对于Web组件，不会在onKeyPreIme返回false时去匹配快捷。而是第三次按键派发中，Web对于未消费的KeyEvent会通过ReDispatch重新派发给ArkUI。在ReDispatch中再执行匹配快捷键等操作。
        
    3.  onKeyEvent & onKeyPreIme：onKeyEvent、onKeyPreIme和onKeyEventDispatch的区别仅在于触发的时机。其中onKeyPreIme的返回值决定了该按键事件后续是否会被继续分发给页面快捷键、输入法、onKeyEventDispatch和onKeyEvent。
        
        当绑定方法的组件处于获焦状态下，外设键盘的按键事件会触发该方法，回调参数为KeyEvent，可由该参数获得当前按键事件的按键行为（KeyType）、键码（keyCode）、按键英文名称（keyText）和事件来源设备类型（KeySource）等信息。
        

#### 问题定位

*   检查组件是否获焦：查看组件类型，搜索其对应API属性对于焦点的设置以及走焦是否正确，如defaultFocus和requestFocus。
*   检查按键事件分发是否正确：
    1.  搜索onKeyPreIme，查看用于触发获焦组件绑定的onKeyPreIme回调和页面快捷键设置是否正确。
        
        该回调的返回值为true时，视作该按键事件已被消费，后续的事件回调（keyboardShortcut、输入法、onKeyEventDispatch和onKeyEvent）会被拦截，不再触发。
        
    2.  搜索keyboardShortcut，查看组件的自定义组合键设置是否正确。
    3.  搜索onKeyEventDispatch和onKeyEvent，查看用于响应onKeyEventDispatch事件和获焦组件绑定的onKeyEvent回调是否设置正确。

#### 分析结论

#### 场景一

由于组件未获取焦点，导致物理键盘输入响应异常。

#### 场景二

由于按键事件数据流中事件分发不正确，如快捷键冲突、对应消费组件不正确等，导致物理键盘输入异常。

#### 修改建议

#### 场景一

使用defaultFocus和requestFocus等方式，确保输入框组件在物理键盘输入时获得焦点，详情请参考支持焦点处理。

#### 场景二

onKeyEvent、onKeyPreIme和onKeyEventDispatch的区别仅在于触发的时机，当绑定方法的组件处于获焦状态下，外设键盘的按键事件会触发上述方法，回调参数为KeyEvent。相关示例可以参考onKeyEvent & onKeyPreIme。

onKeyEvent事件默认是冒泡的（stopPropagation方法可阻止冒泡，参考示例）。

每个按键都有两次回调，分别对应KeyType.Down和KeyType.Up，表示按键被按下、然后抬起。

#### 常见FAQ

Q：在HarmonyOS PC上外接键盘时，如果存在多个键盘输入设备，Caps的状态以哪个键盘为准？

A：本机键盘和外接键盘的Caps按键状态始终保持同步，在任意一个键盘上切换Caps键的状态，另一个键盘会自动同步状态。此机制对numlock、Scroll Lock键也适用。