# ASinput **Repository Path**: nect/asinput ## Basic Information - **Project Name**: ASinput - **Description**: 一个可用于43寸、4K触摸屏的浮动键盘。 - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2026-07-22 - **Last Updated**: 2026-07-22 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 安卓大屏悬浮输入法 需求规格说明书 **版本**:V1.1 **适用屏幕**:43 英寸及以上触摸大屏(16:9 / 16:10),以 DPI 与视距触控规范为技术判据 **目标系统**:Android 8.0 及以上(最低 API 26,目标 API 34) --- ## 修订记录 | 版本 | 日期 | 修订内容 | |------|------|----------| | V1.0 | — | 初版需求规格 | | V1.1 | 2026-07 | 修正焦点/触摸技术矛盾;明确 V1.0 MVP 范围;补充输入场景适配、安全区、权限与测试策略;新增版本路线图 | | V1.2 | 2026-07-21 | 实战内部构建 **v0.1.1** 验收通过:补充拼音候选引擎实现要点(三轮架构/切分DP/词长加权)、4K 大屏键盘高度适配、长按退格行为、悬浮窗显隐状态机与悬浮球(图标/透明度/拖动/位置持久化/点击重弹)的实际需求与落地要点,见第 11 章 | --- ## 1. 项目概述 ### 1.1 背景 在 43 英寸或更大的 Android 触摸交互大屏(如会议一体机、数字标牌、自助服务终端、电子白板)上,传统输入法长期固定停靠屏幕底部,导致: - 遮挡底部关键内容或按钮; - 用户需要弯腰或移动至屏幕下方操作; - 无法适应多人协作、分屏操作等场景。 ### 1.2 产品目标 打造一款**可任意拖动、位置自由**的输入法,支持简体中文、英文输入,为大屏触摸交互提供无遮挡、高效率的文字输入体验。 ### 1.3 适用范围 本需求文档定义了输入法的核心功能、交互方式、技术边界与验收标准。适用于开发、测试及设计团队。功能条目标注 **\[V1.0\]** 表示纳入首个可发布版本(MVP),未标注或标注其他版本号的条目归入后续迭代。 ### 1.4 术语与缩写 | 术语 | 说明 | |------|------| | IME | Input Method Editor,Android 标准输入法框架 | | 悬浮窗 | 通过 `WindowManager` 添加的 `TYPE_APPLICATION_OVERLAY` 类型独立窗口 | | 候选栏 | 显示拼音/英文联想候选词的横向列表区域 | | 悬浮球 | 无输入焦点时键盘缩小后的圆形快捷入口 | | 安全区(Safe Inset) | 扣除状态栏、导航栏、刘海等区域后的可视输入范围 | | MVP | Minimum Viable Product,V1.0 首个可发布版本 | --- ## 2. 用户角色与使用场景 | 角色 | 场景描述 | |------|----------| | 站姿操作者 | 站在大屏前,单手或双手输入,希望键盘跟随手指位置灵活移动 | | 坐姿操作者 | 使用大屏一体机处理文档,键盘在桌面高度,希望键盘可轻移避开视线阻挡 | | 分屏应用用户 | 屏幕左右分屏,键盘可停留在任一应用上方,或居中不干扰两侧 | | 辅助操作者 | 快速输入网址、搜索词、简短信息,无需全键盘占用底部空间 | --- ## 3. 功能性需求 ### 3.1 输入法基础框架 \[V1.0\] - **FR-010**:系统注册为标准输入法(IME),用户可在"语言和输入法"中启用并切换。 - **FR-011**:遵循 Android `InputMethodService` 生命周期,但**不使用默认的底部附着视图**;在 `onCreateInputView()` 与 `onCreateCandidatesView()` 中均返回 `null`,由 `WindowManager` 自行管理键盘与候选栏悬浮窗口。 - **FR-012**:文字提交必须通过 `InputConnection.commitText()` 等标准接口,支持退格、删除、光标移动等编辑操作。 ### 3.2 悬浮与拖动 \[V1.0\] - **FR-020**:键盘以**独立悬浮窗**形式呈现,窗口层级使用 `TYPE_APPLICATION_OVERLAY`。 - **FR-021**:可通过手指长按键盘空白区域并拖动,实时改变键盘在屏幕上的位置。 - **FR-022**:拖动过程中键盘呈现轻微半透明(透明度 80%),松手后恢复不透明。 - **FR-023**:拖动模式下临时添加 `FLAG_NOT_FOCUSABLE`,宿主应用输入焦点不丢失;**按键输入时**移除该标志,键盘区域正常接收并消费触摸事件,确保按键响应。 - **FR-024**:支持键盘快速**贴边吸附**:当键盘边缘距屏幕边缘小于 20dp 时自动吸附到边缘,避免悬空。 - **FR-025**:记录上次键盘**相对位置**(x%、y% 占安全区宽高比例)、缩放比例与布局模式,进程重启后自动恢复;横竖屏切换时按相对坐标重新映射,确保不出屏。 ### 3.3 键盘布局与切换 \[V1.0\] - **FR-030**:默认提供两种键盘模式,可一键切换: - **全键盘(QWERTY)**:适合英文及拼音输入。 - **九宫格(T9)**:适合单手快速中文拼音输入。 - **FR-031**:键盘支持**横向/纵向**自动适配(跟随系统方向或手动锁定)。 - **FR-032**:提供专用切换按键:中/英、数字/符号、返回、换行、空格。 - **FR-033**:按键最小尺寸:触摸热区不小于 48dp × 48dp,大屏设备(≥1080p)建议 56dp 以上,间距 ≥ 8dp,防止误触。 ### 3.4 中文输入 \[V1.0\] - **FR-040**:基于拼音输入,集成轻量级拼音引擎(推荐 **libpinyin** + 开源简体词库;AOSP PinyinIME 仅作备选参考)。 - **FR-041**:候选栏默认显示在键盘上方并跟随键盘移动;用户可在设置中切换为侧方显示。 - **FR-042** \[V1.2\]:支持云输入(可配置开关),本地词库优先。V1.0 不实现,不申请 `INTERNET` 权限。 - **FR-043**:候选词支持翻页,翻页按钮置于候选栏两侧。 - **FR-044**:支持模糊音设置(zh/z、ch/c、sh/s 等)。 - **FR-045**:支持用户词库学习与词频调整(**仅简体中文**)。 - **FR-046** \[V1.0\]:词库与学习功能仅覆盖简体中文,不包含繁体中文。 ### 3.5 英文输入 \[V1.0\] - **FR-050**:英文模式下,全键盘支持大小写切换,双击 Shift 锁定大写,单击切换当前词大小写。 - **FR-051**:英文联想(预测输入),实时显示在候选栏。 - **FR-052**:支持快速输入"-"、"_" 等常用符号。 ### 3.6 数字与符号输入 \[V1.0\] - **FR-060**:独立数字符号布局,通过"?123"键进入。 - **FR-061**:常用符号页可左右滑动切换。 ### 3.7 高级输入功能(后续版本) > 以下功能不纳入 V1.0,详见 §8 版本路线图。 - **FR-070** \[V1.3\]:手写输入模式:支持单字手写识别,手写区域可半透明浮于键盘上方。 - **FR-071** \[V1.3\]:语音输入:调用系统语音识别,结果直接上屏。 - **FR-072** \[V1.2\]:剪贴板快捷短语:用户可预设常用语。 ### 3.8 键盘视觉与自定义 \[V1.0\] - **FR-080**:支持明色/暗色主题,至少内置两套。 - **FR-081**:键盘背景透明度可调(0% ~ 60%),减少对内容的完全遮挡。 - **FR-082**:允许动态调整键盘缩放大小(通过双指捏合或拖动右下角把手),实现 80% ~ 150% 尺寸变化。 ### 3.9 设置与引导 \[V1.0\] - **FR-090**:提供独立设置界面(可从输入法图标或系统设置进入): - 悬浮权限引导(首次使用强制引导)。 - 键盘默认位置、大小重置。 - 模糊音、联想、按键声音/振动等开关。 - 隐私声明(V1.0 无云输入开关,V1.2 起增加)。 - **FR-091**:在输入法未激活或悬浮窗权限未授予时,通过通知或弹窗引导用户开启。 ### 3.10 大屏专属适配 \[V1.0\] - **FR-100**:系统分屏或自由窗口模式下,键盘位置**不自动重置**,保持在拖动位置。 - **FR-101**:当检测到外部物理键盘(USB/蓝牙)连接时,可选择自动隐藏虚拟键盘。 - **FR-102**:支持键盘在无输入焦点 5s 后自动缩小为悬浮球,点击 300ms 内唤出完整键盘(减小常驻占用面积)。 ### 3.11 输入场景区配 \[V1.0\] - **FR-110**:根据 `EditorInfo.inputType` 自动切换键盘模式:`TYPE_CLASS_NUMBER` 自动弹出数字键盘;`TYPE_TEXT_VARIATION_PASSWORD` / `TYPE_TEXT_VARIATION_VISIBLE_PASSWORD` 禁用词库学习。 - **FR-111**:根据 `imeOptions` 动态显示动作键(完成 / 搜索 / 下一步 / 发送等)。 - **FR-112**:`inputType` 为 `TYPE_NULL` 或 `onFinishInput()` 后,键盘自动收起或进入悬浮球状态。 - **FR-113**:多行文本框(`TYPE_TEXT_FLAG_MULTI_LINE`)显示换行键;单行文本框显示动作键。 ### 3.12 安全区与多窗口适配 \[V1.0\] - **FR-120**:键盘与候选栏不得遮挡系统状态栏、导航栏、刘海区域,使用 `WindowInsets` 约束在安全区内。 - **FR-121**:贴边吸附后仍须保持 ≥ 8dp 内边距,确保可再次拖动。 - **FR-122**:自由窗口 / 分屏下,键盘限制在当前 `Display` 可见区域内,不跨越屏幕边界。 - **FR-123**:位置记忆使用相对坐标(x%、y%)+ 缩放比例存储;横竖屏切换后按屏幕新尺寸重新映射,保证相对位置不变。 ### 3.13 按键交互细节 \[V1.0\] - **FR-130**:长按按键弹出备选字符(如标点、数字上标),松手选择。 - **FR-131**:退格短按删除单个字符,长按(≥400ms)连续删除。 - **FR-132** \[Nice-to-have\]:空格键上滑快速切换中英文(V1.0 可选实现,不影响验收)。 - **FR-133**:双击 Shift 锁定大写(同 FR-050),再次单击 Shift 解除锁定。 --- ## 4. 非功能性需求 ### 4.1 性能 - 键盘弹出首帧 ≤ 100ms,隐藏 ≤ 100ms;拖动跟手延迟 ≤ 50ms;动画帧率 ≥ 60fps。 - 拼音检索上屏时间 ≤ 30ms(本地词库)。 - 内存占用:常驻 ≤ 80MB(拼音引擎 + 词库约 30~50MB,预留浮动空间)。 ### 4.2 兼容性 - 系统:Android 8.0 ~ 14+,适配 64 位。 - 分辨率:1080p、2K、4K,适配不同 DPI(mdpi 至 xxxhdpi)。 - 主流大屏设备:如皓丽、MAXHUB、华为 IdeaHub 等,无明显输入框不兼容问题。 ### 4.3 可用性 - 符合安卓辅助功能标准,可被 TalkBack 等读屏软件识别。 - 所有功能触控目标 ≥ 48dp,间距 ≥ 8dp。 - 界面文字清晰,对比度符合 WCAG AA 标准。 ### 4.4 安全与隐私 - **绝不**记录任何密码字段输入内容(通过 `InputType` 判断)。 - 云输入功能(V1.2)默认关闭,启用时明确告知数据传输范围与用途。 - 用户词库仅存储在本地,不上传,无账号体系。 ### 4.5 稳定性 - 输入法服务崩溃后,系统能自动恢复,不影响宿主应用。 - 异常场景(如悬浮窗权限突然被回收)需在 3s 内降级为底部附着模式并以 Toast 提示用户。 ### 4.6 权限清单 | 权限 | 用途 | V1.0 | |------|------|------| | `SYSTEM_ALERT_WINDOW` | 悬浮键盘窗口 | 必须 | | `VIBRATE` | 按键振动反馈 | 可选 | | `INTERNET` | 云输入(FR-042) | 不申请 | | `RECORD_AUDIO` | 语音输入(FR-071) | 不申请 | ### 4.7 数据存储 - **设置项**:`SharedPreferences` 存储主题、模糊音、位置比例、缩放比例等。 - **用户词库**:本地 SQLite 数据库,存储词频与学习记录。 - **存储路径**:`/data/data//files/`,仅应用私有目录。 - **生命周期**:卸载应用即清除全部数据;V1.0 无云同步、无账号体系。 --- ## 5. 交互设计关键指引 1. **拖动解锁**:长按键盘空白区(未点中按键)150ms 后触发拖动,短按不触发,避免与按键混淆。 2. **单点与多点**:拖动过程仅支持单指,多指触控忽略,防止误拖。 3. **避让原则**:候选栏默认在键盘上方;若键盘上边缘距屏幕顶部 < 候选栏高度 + 8dp,则候选栏自动移至键盘下方;左右贴边时候选栏仍保持水平居中于键盘。 4. **旋转避让优先级**:旋转后先按 FR-123 重映射位置 → 应用 `WindowInsets` 安全区约束 → 执行候选栏上下避让 → 最后执行贴边吸附(FR-024)。 5. **返回键逻辑**:键盘无焦点时按返回键可最小化键盘为悬浮球,而非直接关闭。 6. **触摸消费规则**:按键区域必须消费触摸事件(`return true`);键盘非按键空白区在拖动模式外不拦截宿主应用操作。 --- ## 6. 技术约束与实现路线 ### 6.1 核心架构 - **双模块组合**:`InputMethodService`(生命周期与 `InputConnection`)+ `WindowManager`(悬浮视图渲染)。 - **服务生命周期联动**: - `onStartInput()` → 创建/显示悬浮窗,恢复位置,绑定 `InputConnection`。 - `onFinishInput()` → 隐藏键盘或切换为悬浮球。 - `onConfigurationChanged()` → 按 FR-123 重映射相对坐标,刷新布局。 - `onDestroy()` → 移除悬浮窗,释放资源。 - **`showSoftInput()` / `hideSoftInput()`**:与悬浮窗显隐联动,不依赖系统默认 IME 窗口。 ### 6.2 窗口状态机 ``` Hidden → onStartInput → KeyboardShown KeyboardShown → 长按空白区 150ms → Dragging(半透明 + FLAG_NOT_FOCUSABLE) Dragging → 松手 → KeyboardShown(吸附 + 恢复不透明) KeyboardShown → onFinishInput → FloatingBall FloatingBall → 点击 → KeyboardShown KeyboardShown → 返回键 → FloatingBall KeyboardShown → 权限丢失 → BottomFallback(底部附着降级) ``` ### 6.3 悬浮窗创建 - **API 30+**:使用 `createDisplayContext(display).createWindowContext(TYPE_APPLICATION_OVERLAY, null)` 创建 `WindowContext`,确保资源配置正确。 - **API 26~29 降级**:使用 `ApplicationContext` 获取 `WindowManager`,需注意多显示器场景下配置偏差,V1.0 以主屏幕(`Display.DEFAULT_DISPLAY`)为主。 - **窗口类型**:`TYPE_APPLICATION_OVERLAY`(需 `SYSTEM_ALERT_WINDOW` 权限)。 ### 6.4 窗口 Flags 组合 | 状态 | Flags | |------|-------| | 正常输入 | `FLAG_NOT_TOUCH_MODAL` \| `FLAG_ALT_FOCUSABLE_IM` | | 拖动中 | 上述 + `FLAG_NOT_FOCUSABLE` | | 悬浮球 | `FLAG_NOT_FOCUS_MODAL` \| `FLAG_NOT_FOCUSABLE` | > `FLAG_ALT_FOCUSABLE_IM` 是悬浮 IME 与宿主应用共存的关键标志,防止干扰系统软键盘焦点链。 > 按键时**不得**设置 `FLAG_NOT_FOCUSABLE`,确保触摸事件被键盘视图正确消费。 ### 6.5 中文引擎 - **推荐方案**:libpinyin + 开源简体词库(如搜狗细胞词库格式的合法开源替代)。 - **备选方案**:AOSP PinyinIME 移植(维护成本较高,不作为首选)。 ### 6.6 拖动处理 - 自定义 `OnTouchListener`,在 `ACTION_MOVE` 中计算偏移并更新 `WindowManager.LayoutParams.x/y`。 - 坐标单位使用 px 存储,界面展示与验收以 dp 换算,保证不同 DPI 设备物理尺寸一致。 ### 6.7 设备适配 - 使用 `Resources.getSystem().displayMetrics` 与 `WindowInsets` 获取实际可视区域。 - 所有尺寸规范以 dp 为准,不写死 px 值。 ### 6.8 模块划分(开发参考) | 模块 | 职责 | |------|------| | `ime-service` | `InputMethodService` 生命周期、`InputConnection` 交互 | | `overlay-window` | `WindowManager` 悬浮窗创建、Flags 管理、状态机 | | `keyboard-ui` | 全键盘 / 九宫格 / 符号布局、主题、缩放 | | `pinyin-engine` | libpinyin 封装、候选词检索、模糊音 | | `user-dict` | SQLite 用户词库、词频学习 | | `settings` | 设置 Activity、权限引导流程 | --- ## 7. 验收标准 | 编号 | 验收项 | 标准 | |------|--------|------| | AC01 | 悬浮拖动 | 在 43 寸大屏上,可任意位置拖动键盘,宿主应用焦点不丢失、无残影 | | AC02 | 输入功能 | 中英文切换、简体拼音输入、英文联想、退格、换行均正常 | | AC03 | 悬浮权限 | 首次启动自动请求,拒绝后引导开启;权限收回时 3s 内降级为底部模式 | | AC04 | 性能 | 键盘弹出首帧 ≤ 100ms,拖动无肉眼可见卡顿 | | AC05 | 兼容测试 | 在 3 款以上不同品牌安卓大屏上正常安装、启用、输入 | | AC06 | 安全合规 | 密码字段不记录词库、不学习词频 | | AC07 | 界面自适应 | 横竖屏切换键盘布局、大小不变形,相对位置比例不变 | | AC08 | 输入场景 | 密码框不学习、数字框自动数字键盘、搜索框显示搜索键 | | AC09 | 安全区 | 贴边 / 旋转后键盘与候选栏不超出可视区域 | | AC10 | 降级 | 撤销悬浮权限后 3s 内切换底部模式并 Toast 提示 | | AC11 | 悬浮球 | 无焦点 5s 后缩小为悬浮球,点击 300ms 内恢复完整键盘 | | AC12 | 记忆 | 杀进程重启后位置误差 ≤ 2% 屏幕宽高 | --- ## 8. 版本路线图 | 版本 | 目标时间 | 范围 | |------|----------|------| | **V1.0(MVP)** | 2026-07 ~ 2026-09 | 悬浮拖动、中英文输入、简体拼音、基础设置、悬浮球、权限引导 | | **V1.1** | 2026-10 | 体验打磨:主题扩展、性能优化、厂商 ROM 兼容补丁 | | **V1.2** | 2026-11 ~ 2026-12 | 云输入(FR-042)、剪贴板快捷短语(FR-072) | | **V1.3** | 2027-01 ~ 2027-02 | 手写输入(FR-070)、语音输入(FR-071) | --- ## 9. 测试策略 ### 9.1 单元测试 - 拼音引擎:检索速度、模糊音匹配、候选排序。 - 坐标映射:相对坐标 ↔ 绝对像素转换、旋转重映射。 - 词库读写:学习记录、词频更新、密码框隔离。 ### 9.2 仪器测试(Instrumented) - `InputConnection` 文本提交、退格、光标移动。 - 横竖屏旋转、分屏模式、多窗口模式。 - 悬浮窗权限授予 / 撤销的降级流程。 ### 9.3 设备矩阵 - **品牌**:皓丽、MAXHUB、华为 IdeaHub(≥ 3 款)。 - **分辨率**:1080p、4K。 - **方向**:横屏为主,竖屏抽检。 ### 9.4 应用兼容性回归 - 系统应用:Chrome 浏览器、系统设置。 - 第三方:WPS、钉钉、飞书等大屏常见办公 / 协作应用。 - 验证项:弹出、输入、切换、旋转、分屏无异常。 --- ## 10. 明确排除范围(Out of Scope) 以下功能不在本项目任何版本规划中,除非另有变更通知: - 繁体中文输入 - 五笔、仓颉等非拼音输入方案 - 表情 / Emoji 独立面板 - OCR 拍照输入 - 多用户云同步 / 账号体系 - iOS、Windows、Web 等非 Android 平台 --- ## 11. 实战实现要点(内部构建 v0.1.1,2026-07-21 验收) > 本章记录 V1.0/V1.1 需求在真机(43 寸 4K 大屏 + 真机)与雷电模拟器实战落地过程中, > 经用户反馈、真机验证后确认的**需求边界与实现要点**。对应 APK:`app-debug.apk` > (`versionCode=2`,`versionName="0.1.1"`)。 ### 11.1 拼音候选引擎 **需求**:拼音输入候选必须完整、排序合理、可翻页;常用字(过/国/果等)必须在首页出现; 选过某字后不影响其他同音字候选;支持不完整拼音前缀联想(如 `guo`→`guojia`)。 **词库来源**:rime-ice 开源简体词库 - 构建脚本:`tools/build_dict.py` - 源文件:`tools/dict_src/{8105.dict.yaml, base.dict.yaml, base_jd.yaml, custom.txt}` - 产物:`dict_chars.txt`(8,528 单字→拼音映射,用于切分回退);`dict_words.txt`(358,602 词 / 293,290 拼音键,词组精确匹配) - 用户词库:本地 SQLite `user_dict.db`(选词后自动学习) **实现要点(对应 FR-040 / FR-043 / FR-045)**: 1. **三轮候选架构**(PinyinEngine.getCandidates): - 第一轮:系统词组精确匹配(`wordDict`) - 第二轮:**始终执行**回退策略(音节切分 + 前缀匹配),不受用户词库影响 - 第三轮:用户词库仅做权重增强(`freq + 500`),**不阻断**回退 - 要点:用户词库查询必须放在回退之后,否则会阻断系统回退(曾导致选过「萋」后 `qi` 只剩「萋」、`fei` 无候选) 2. **音节切分 DP(segmentToChars)**:采用**最小化音节数**(最长优先匹配)。 - 旧实现用最大化音节数导致 `guo` 被切成 `[gu, o]` 生成「古哦」等无意义组合; - 修正后 `guo→[guo]`、`guojia→[guo,jia]`、`xian→[xian]` 3. **候选排序**:`score = freq / len^0.7`(词长加权,短词优先)。 - 修复 `canl` 场景下「灿烂辉煌(4 字)」排在「参量(2 字)」之前的问题 4. **翻页**:`pageSize = 9`,候选栏两侧 ◀/▶ 翻页按钮;`guo` 验证返回 21 候选 / 3 页,首 5 = 过 国 果 郭 锅 ### 11.2 4K 大屏键盘高度适配 **需求**(对应 FR-033 / FR-100):4K 屏下 fallback 模式(无 overlay 权限或 `FLAG_SECURE` 安全窗口)键盘高度与按钮过小,2K 正常。 **实现要点**(KeyboardView.rebuild): - `fillParentWidth` 模式下 `effectiveScaleY = effectiveScaleX.coerceIn(1f, 2.0f)` - 高度跟随宽度等比放大(最大 2 倍),旧实现 `effectiveScaleY` 被 `coerceAtLeast(1f)` 钳制为 1,导致高度永远不放大 ### 11.3 长按退格行为 **需求**(对应 FR-131):英文状态长按退格连续删除,删空后**不应**继续触发删除 + 按键音/震动。 **实现要点**(KeyboardController): - `handleBackspace()` 与连续删除 `Runnable` 均增加前置检查: `composingPinyin` 与 `englishBuffer` 均为空时直接 `stopContinuousBackspace()` 并返回 - 首延迟 400ms、间隔 50ms 的连续删除节奏保持不变 ### 11.4 悬浮窗显隐状态机(overlay 模式) **需求**(对应 FR-102 / FR-112 / FR-120): - 无输入框焦点时浮窗不显示、不遮挡界面 - 点「▼收起」后切换页面(如 Chrome 切 Tab)浮窗**不应**自动重新弹出 - 点浮球或聚焦**新**文本框应重新弹出键盘 - 安全输入框(`FLAG_SECURE`,如 WiFi 密码框)进入 fallback 后隐藏「悬浮」切换按钮 **实现要点**(FloatingInputMethodService + OverlayWindowManager): 1. **活跃会话信号只能用 `onStartInput`**:悬浮模式下 `onEvaluateInputViewShown()` 返回 `false`,系统**不会**回调 `onStartInputView`,若把「有焦点」标记放在 `onStartInputView` 里会导致键盘与浮球永远不显示(曾出现此回归) 2. **三态显隐**(enterOverlayMode): - `userManuallyCollapsed && hasActiveEditingSession` → 只显示浮球(不自动弹键盘,避免切页面复弹) - `hasActiveEditingSession` → 显示键盘 - 其余 → 全隐藏 3. **`userManuallyCollapsed`(用户手动收起)标记清除时机**: - 点浮球主动唤起键盘时清除(OverlayWindowManager `onUserExpanded` 回调) - `onStartInput` 中当 `!restarting`(**新的文本框聚焦**)时清除 → 收起后点其他文本框重弹键盘 - **不**在 `onStartInput` 无条件清除,否则切页面触发 `onStartInput` 会把它清掉 → 键盘复弹 - 同一输入框被动重连(`restarting == true`,如切 Chrome 页面)保持只显示浮球 4. **安全输入框**:`enterFallbackMode()` 中 `canSwitchToOverlay = hasPermission() && !currentIsSecure` ### 11.5 悬浮球 **需求**(对应 FR-102):无焦点时缩为圆形快捷入口;可识别(带图标);透明度可随设置;可自由拖动并**记忆位置**。 **实现要点**(OverlayWindowManager.showFloatingBall / setupBallDrag): 1. **图标**:`FrameLayout` 包裹蓝圈背景 + ⌨️ 文本图标(24sp),替代纯色圆点 2. **透明度**:`ball.alpha = 1 - keyboardOpacity / 100`,与悬浮窗共享同一透明度设置 3. **拖动 + 点击区分**:`OnTouchListener` 内用 `moved` 标志区分"拖动"(移动 > 8px)与"轻触点击"; - ⚠️ 教训:给 View 同时设 `OnTouchListener(return true)` 与 `setOnClickListener` 时,点击会被吞掉(消费 `ACTION_UP` 后框架不派发 `onClick`),必须在 touch 监听内自行判定点击(曾导致点浮球键盘不弹) - 轻触 → 清除收起标记 + 移除浮球 + 唤起键盘;拖动 → 松手吸附最近边缘(`PositionManager.snapToEdge`) 4. **位置持久化**:`SettingsManager.ballXPercent / ballYPercent`(占安全区可用空间百分比)存入 `SharedPreferences`; `snapBallToEdge()` 拖动结束写入,`showFloatingBall()` 下次恢复;「重置位置」一并归位 ### 11.6 版本与设置页 - `app/build.gradle.kts`:`versionCode = 2`,`versionName = "0.1.1"` - 设置页版本信息:位置由布局底部移至「启用输入法」卡片紧下方,字号 13sp → 18sp 加粗(适配 4K 可读性) ### 11.7 v0.1.1 验收结论 用户于 2026-07-21 19:20 确认本版本"非常完美",以下要点全部通过真机/模拟器验证: | 模块 | 验收状态 | |------|----------| | 拼音候选质量(guo / zhong / qi / fei / canl 等) | ✅ | | 4K 大屏键盘高度 | ✅ | | 长按退格空内容停止发声 | ✅ | | 无焦点 / 切页面浮窗不自动弹 | ✅ | | 浮球:图标 + 透明度 + 可拖动 + 位置持久化 | ✅ | | 点浮球重弹键盘 + 收起后点新文本框重弹键盘 | ✅ | > 可视为当前稳定版本;后续如需正式发布再走 release 签名构建。 --- **文档结束**。开发过程中可根据原型反馈迭代修改,变更须同步更新修订记录。