焦点如何在窗口之间切换
2026/8/6大约 3 分钟WMS 窗口生命周期Android 14updateFocusedWindowLockedIME target焦点
焦点如何在窗口之间切换
用户能看到某个窗口,不代表它一定持有按键焦点或成为 IME target。WMS 会根据可聚焦属性、 窗口层级和当前转换状态选择焦点,再通知输入系统与客户端;可见性、按键目标和输入法目标是 三个相关但可以分离的状态。
先分清可见窗口、焦点窗口与 IME target
| 常见误解 | 正确理解 |
|---|---|
| 当前可见的窗口一定获得焦点 | 焦点是独立状态,取决于窗口是否可聚焦、是否最近添加等条件 |
| 焦点只有一个概念 | 窗口焦点、按键输入目标、IME target 是三个不同状态 |
可见性回答“用户能不能看到”,焦点回答“系统把键事件发给谁”,IME target 回答“输入法要跟着哪个窗口弹”。三者经常一致,但可以分离:一个不可聚焦的窗口可以可见,一个不接收按键的窗口仍可以是 IME target。
焦点查找与更新
逐步解释:
- ① 触发源:加窗、移除、可见性或层级变化都可能让焦点失效。
- ② 入口:
DisplayContent.updateFocusedWindowLocked()(DisplayContent.java:3953)统一处理焦点重算。 - ③ 查找:
findFocusedWindow()(同文件 3927 行)按容器树遍历,找到第一个满足条件的窗口。 - ④ 比较:与当前焦点相同则跳过通知。
- ⑤ 通知:焦点变化通知新旧窗口客户端。
- ⑥ IME:调用
computeImeTarget()(同文件 4188 行)重算输入法目标。 - ⑦ 输入:标记需要更新输入窗口信息,供 InputMonitor 使用。
窗口焦点候选条件
WindowState.canReceiveKeys()(WindowState.java:2886)判断窗口是否可接收按键。r75 中它要求窗口处于可见请求或正在添加状态、可见、可聚焦且不是瞬态等(WindowState.java:2905-2919)。因此:
- 不可聚焦的窗口(
FLAG_NOT_FOCUSABLE)永远不是焦点候选。 - 被遮挡的窗口如果满足条件,仍可能保留焦点。
- Dialog 弹出时,Dialog 窗口通常可聚焦,焦点从底层 Activity 切走。
三个场景
| 场景 | 焦点结果 | IME target |
|---|---|---|
| Activity 切换 | 新 Activity 的主窗口获得焦点 | 新窗口成为 IME target |
| Dialog 弹出 | Dialog 获得焦点,底层窗口失焦 | 若 Dialog 有输入框,IME 跟随 Dialog |
| 输入法窗口出现 | 窗口焦点不变 | IME target 指向可编辑窗口,IME 浮层本身不抢焦点 |
第三个场景最反直觉:输入法窗口显示在屏幕上,但焦点仍属于背后的编辑框,否则用户敲键盘就不会输入到目标框。
源码证据
阅读目标:确认焦点是 Display 级查找结果,且与输入窗口信息更新是相邻动作。
正文指针:DisplayContent.java:3953 / 3927 / 4188;WindowManagerService.java:1813。
// DisplayContent.java:3953
boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows,
int topFocusedDisplayId) {// DisplayContent.java:3927
WindowState findFocusedWindow() {
...
forAllWindows(mFindFocusedWindow, true /* traverseTopToBottom */);这段代码证明: 焦点通过遍历容器树查找,而不是“谁最上层谁得焦点”;updateFocusedWindowLocked 是 DisplayContent 上的统一入口。
源码入口
源码基准:android-14.0.0_r75。
以下文件默认位于 frameworks/base/services/core/java/com/android/server/wm/,表中省略该前缀:
| 文件 | 关键位置 | 作用 |
|---|---|---|
DisplayContent.java | 3927 行 findFocusedWindow();3953 行 updateFocusedWindowLocked();4188 行 computeImeTarget() | 焦点查找、更新与 IME target |
WindowState.java | 2886 行 canReceiveKeys();2905 行 canReceiveKeys(boolean) | 焦点候选条件 |
WindowManagerService.java | 1813 行 updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS, ...) | 加窗时焦点更新 |
InputMonitor.java | 367 行 setInputFocusLw(...) | 输入焦点同步 |