当前位置: 首页 > article >正文

预上屏是什么鬼?KikaInputMethod 输入预测功能深度解析

文章目录预上屏的本质预上屏执行流程核心预上屏代码Enter 键确认上屏光标操作全集私有命令通信sendPrivateCommand物理键盘处理onKeyDownInputClient 关键接口速查踩坑记录写在最后用搜狗或者系统键盘打字时打到一半会出现候选词框选中候选词之前的虚显效果就叫预上屏Preview Text。KikaInputMethod demo 里用了一套完整的预上屏实现还有文字选择、光标移动等进阶操作。预上屏的本质正常打字用户按一个键 → 字符立即出现在文本框里已上屏预上屏用户按了几个键 → 先显示正在候选的文字虚线下划线样式→ 用户选择候选词 → 候选内容正式上屏或者修改后上屏用大白话说预上屏就是先占位确认后再落地。预上屏执行流程核心预上屏代码KeyboardController.ets里的insertText方法publicinsertText(text:string):void{if(!this.mTextInputClient)return;try{// 第一步普通插入字符立即上屏this.mTextInputClient.insertTextSync(text);// 第二步获取光标位置letindexCursor:numberthis.mTextInputClient.getTextIndexAtCursorSync();// 第三步读取光标前 2 个字符letlength2;lettextPre:stringthis.mTextInputClient.getForwardSync(length);// 注意getForwardSync 不包含刚插入的字符需要手动加上textPretextPretext;if(textPrehel){// 打了 hel触发预上屏 hello world// range 的 end 要 1 把当前字符也包含进选区letrange:inputMethodEngine.Range{start:indexCursor-length,end:indexCursor1};// 设置预上屏把 range 区域的内容替换为 hello world 的虚显this.mTextInputClient.setPreviewText(hello world,range).then((){console.log(预上屏设置成功);});}elseif(this.intputText.lengthlength1){// 已经在预上屏过程中继续打字letindexSubStrStartthis.intputText.lastIndexOf(hel);if(indexSubStrStart0){letsubStrthis.intputText.substring(indexSubStrStart,indexCursor);// 如果当前已输入内容是 hello world 的前缀继续更新预上屏if(subStr!hello worldhello world.includes(subStr)){letrange:inputMethodEngine.Range{start:indexSubStrStart,end:indexCursor1};this.mTextInputClient.setPreviewText(hello world,range);}}}}catch(err){console.error(insertText error:${JSON.stringify(err)});}}Enter 键确认上屏publicsendKeyFunction():void{if(this.mTextInputClientthis.mEditorAttribute){// 根据编辑框的 enterKeyType 发送对应功能// enterKeyType: 0默认换行, 1搜索, 2发送, 3下一项, 4完成this.mTextInputClient.sendKeyFunction(this.mEditorAttribute.enterKeyType).then((){console.log(sendKeyFunction 成功);});// 结束预上屏状态让虚显的文字正式落地this.mTextInputClient.finishTextPreview().then((){console.log(finishTextPreview 成功);});}}光标操作全集InputHandler 里封装了完整的光标操作这些是输入法进阶功能的基础import{inputMethodEngine}fromkit.IMEKit;// 移动光标上/下/左/右publicmoveCursor(direction:inputMethodEngine.Direction):void{// Direction.CURSOR_UP/DOWN/LEFT/RIGHTthis.mTextInputClient?.moveCursor(direction);}// 光标移到开头publicasyncmoveCursorToBegin():Promisevoid{// selectByRange({start:0, end:0}) 等效于光标移到 0 位置awaitthis.mTextInputClient?.selectByRange({start:0,end:0});}// 光标移到末尾publicasyncmoveCursorToEnd():Promisevoid{// 用 1000 作为无穷大实际上文本没有 1000 字符时会停在末尾awaitthis.mTextInputClient?.selectByRange({start:1000,end:1000});}// 按方向选中文本Shift 方向键的效果publicselectByMovement(direction:inputMethodEngine.Direction):void{this.mTextInputClient?.selectByMovement({direction:direction});}// 从当前光标位置选到开头publicasyncselectToBegin():Promisevoid{letindexawaitthis.mTextInputClient!.getTextIndexAtCursor();if(index0){awaitthis.mTextInputClient?.selectByRange({start:0,end:index});}}// 从当前光标位置选到末尾publicasyncselectToEnd():Promisevoid{letindexawaitthis.mTextInputClient!.getTextIndexAtCursor();awaitthis.mTextInputClient?.selectByRange({start:index,end:1000});}私有命令通信sendPrivateCommand输入法和应用之间可以通过私有命令传递自定义数据// 输入法端发送私有命令给宿主应用publiconInputStart():void{letrecord:Recordstring,inputMethodEngine.CommandDataType{previewTextStyle:underline// 自定义字段};this.mTextInputClient?.sendPrivateCommand(record);}// 输入法端接收宿主应用发来的私有命令inputMethodAbility.on(privateCommand,(record:Recordstring,inputMethodEngine.CommandDataType){Object.keys(record).forEach((key:string){console.log(key:${key}, value:${record[key]});});});这个功能适合做输入法和应用之间的定制化协议比如应用告诉输入法我是密码框只显示数字键盘。物理键盘处理onKeyDown当外接了蓝牙键盘或设备本身有实体键盘时KeyboardDelegate的keyDown事件会触发publiconKeyDown(keyEvent:inputMethodEngine.KeyEvent):boolean{letkeyCodekeyEvent.keyCode;// 维护当前按下的键列表用于检测组合键this.keyCodes.push(keyCode);// 处理功能键letkeyValuegetHardKeyValue(keyCode,this.isShiftKeyHold());returnthis.inputHardKeyCode(keyValue,keyCode);}publicprocessFunctionKeys(keyValue:string):boolean{switch(keyValue){caseKEYCODE_DEL:this.inputHandle.deleteForward(1);returntrue;// 返回 true 消费系统不再处理caseKEYCODE_FORWARD_DEL:this.inputHandle.deleteBackward(1);returntrue;caseKEYCODE_DPAD_UP:this.inputHandle.moveCursor(inputMethodEngine.Direction.CURSOR_UP);returntrue;caseKEYCODE_DPAD_LEFT:this.inputHandle.moveCursor(inputMethodEngine.Direction.CURSOR_LEFT);returntrue;// ...default:returnfalse;// 返回 false 不消费交给系统}}InputClient 关键接口速查接口用途同步/异步insertTextSync(text)插入文字同步deleteForward(n)向前删 n 个字符异步deleteBackward(n)向后删 n 个字符异步getForwardSync(n)获取光标前 n 个字符同步getBackwardSync(n)获取光标后 n 个字符同步getTextIndexAtCursorSync()获取光标位置同步moveCursor(direction)移动光标异步selectByRange(range)选中范围异步setPreviewText(text, range)设置预上屏异步finishTextPreview()结束预上屏异步sendKeyFunction(enterKeyType)发送 Enter 功能异步getEditorAttribute()获取编辑框属性异步sendPrivateCommand(record)发送私有命令异步sendExtendAction(action)发送扩展操作剪切/复制/全选等异步踩坑记录坑1getForwardSync 不包含刚插入的字符insertTextSync后立即调getForwardSync读出来的内容不包含刚插入的那个字符。原因是预上屏状态下字符在虚显区域还没正式提交。代码里需要手动textPre textPre text补上。坑2selectByRange({start:1000, end:1000}) 是移动光标到末尾的常见技巧没有直接的光标到末尾接口用selectByRange传一个大于文本长度的值即可系统会自动截断到实际末尾。坑3finishTextPreview 必须在 sendKeyFunction 之后调如果反过来预上屏内容会被清空Enter 键的功能也不会正常触发。坑4私有命令的 value 类型只支持 boolean/number/stringCommandDataType是boolean | number | string的联合类型不能传对象或数组。复杂数据需要序列化成字符串再传。写在最后预上屏功能让输入法的候选词体验更流畅避免了候选词替换后光标位置错误的问题。虽然 demo 里只是个hel → hello world的演示但背后的接口和机制是完整的照着这个框架实现真实的拼音/联想输入完全可行。

相关文章:

预上屏是什么鬼?KikaInputMethod 输入预测功能深度解析

文章目录预上屏的本质预上屏执行流程核心预上屏代码Enter 键确认上屏光标操作全集私有命令通信(sendPrivateCommand)物理键盘处理(onKeyDown)InputClient 关键接口速查踩坑记录写在最后用搜狗或者系统键盘打字时,打到一…...

CANopen调试实战:当SDO读写失败时,如何像老司机一样快速读懂Abort报文里的错误码?

CANopen调试实战:SDO读写失败时快速解析Abort报文错误码 调试CANopen设备时,SDO通信失败是最常见的痛点之一。当设备返回Abort报文,屏幕上那一串十六进制代码往往让工程师陷入迷茫——是对象字典配置错误?还是网络通信问题&#…...

新手别怕!用51单片机+74HC138/573点亮静态数码管,保姆级代码+仿真(Keil C51)

从零玩转51单片机:静态数码管驱动全攻略(74HC13874HC573实战) 第一次拿到51单片机开发板时,看到原理图上密密麻麻的74HC138、74HC573芯片标识,很多初学者都会感到无从下手。这些看似复杂的数字芯片,实际上是…...

一键部署童年回忆:用1Panel面板轻松构建在线DOS游戏库

1. 为什么你需要一个在线DOS游戏库? 记得小时候偷偷在电脑课打开《仙剑奇侠传》的快乐吗?或者为了通关《金庸群侠传》熬夜到凌晨的疯狂?这些经典DOS游戏承载着太多80、90后的集体记忆。但如今想在现代电脑上运行这些老游戏,光是配…...

别再手动画图了!用Project 2003为你的软件项目做个专业甘特图(附详细步骤与资源分配技巧)

经典工具新生命:用Project 2003打造专业级软件项目甘特图 在软件工程领域,项目管理工具的选择往往让人陷入两难:现代平台功能繁杂学习曲线陡峭,而Excel等基础工具又难以满足专业需求。这时,一款被遗忘的经典——Micros…...

Kubernetes Operator开发实战

Kubernetes Operator开发实战 一、Operator概述 Kubernetes Operator是一种软件扩展模式,用于管理复杂的有状态应用。 1.1 Operator模式 ┌──────────────────────────────────────────────────────────…...

Elasticsearch聚合查询优化实战

Elasticsearch聚合查询优化实战 一、聚合查询概述 Elasticsearch的聚合功能是数据分析的核心,支持多种聚合类型来满足不同的分析需求。 1.1 聚合类型 类型说明使用场景Metric指标聚合求和、平均值、最大值、最小值Bucket桶聚合分组统计、区间统计Pipeline管道聚合基…...

从‘盲猜’到‘先知’:深度解读神经RRT*如何让采样规划拥有‘大局观’

神经RRT*:当路径规划算法学会"思考"的范式革命 在自动驾驶汽车寻找最短路径、无人机规划避障航线的场景中,传统RRT算法就像一位盲人摸象的探险者——它通过随机撒点的方式探索环境,虽然最终能找到出路,却需要耗费大量时…...

保姆级教程:在Windows上跑通Deeplabv3+,用Cityscapes数据集训练语义分割模型(附避坑指南)

保姆级教程:在Windows上跑通Deeplabv3,用Cityscapes数据集训练语义分割模型(附避坑指南) 语义分割作为计算机视觉领域的核心技术之一,正在自动驾驶、医疗影像分析等领域发挥越来越重要的作用。而Deeplabv3作为语义分割…...

告别传统知识蒸馏:用CVPR2022的‘逆向蒸馏’在PyTorch里玩转工业异常检测

工业级异常检测实战:基于CVPR2022逆向蒸馏的PyTorch实现指南 当传统知识蒸馏在工业缺陷检测中遭遇瓶颈——学生网络对异常样本产生"幻觉响应"、模型对微小缺陷敏感度不足、复杂纹理场景下误报率飙升——CVPR2022提出的逆向蒸馏架构犹如一剂精准的手术刀。…...

山海再赴,探索向新|2026 第二届搜狐极限探索者大会盛大启航!

2025年6月5日,由搜狐主办的首届搜狐极限探索者大会在北京盛大举行。大会以“致敬极限探索者”(Salute to the Ultimate Explorers)为主题,汇聚中国上百位各极限运动领域顶尖的探索者、企业及明星嘉宾,通过巅峰演讲、深…...

Bifrost三星固件下载器:免费跨平台获取官方系统的一站式解决方案

Bifrost三星固件下载器:免费跨平台获取官方系统的一站式解决方案 【免费下载链接】Bifrost Cross-platform tool for downloading Samsung mobile device firmware. 项目地址: https://gitcode.com/gh_mirrors/sa/Bifrost 你是否曾为寻找三星设备官方固件而烦…...

从‘盲人摸象’到‘全局视野’:手把手教你用MATLAB/Simulink仿真PSO-MPPT对抗光伏遮荫(避坑指南)

从‘盲人摸象’到‘全局视野’:手把手教你用MATLAB/Simulink仿真PSO-MPPT对抗光伏遮荫(避坑指南) 光伏发电系统在局部遮荫条件下,功率-电压特性曲线会呈现多峰值现象,传统MPPT算法容易陷入局部最优。粒子群优化&#x…...

SPICE仿真实战:从时序分析基础到建立保持时间验证

1. 项目概述:从“香料”到“时序”的工程思维“时序分析基本概念介绍”这个标题,乍一看可能有点割裂。前半部分“时序分析基本概念介绍”指向一个非常经典且基础的电子工程领域——信号在时间维度上的行为分析,这是电路设计、通信系统乃至嵌入…...

5元级MCU Air601实战评测:硬件兼容、LuatOS开发与ESP12F迁移指南

1. 项目概述:一颗5元级MCU的“越级”挑战最近在捣鼓一个智能家居的小玩意儿,原本计划用ESP12F(也就是我们常说的ESP8266模组)来做,毕竟它生态成熟,资料遍地都是。但在采购物料时,偶然瞥见了合宙…...

【计算机毕业设计】基于Springboot的工作流程管理系统设计与实现+万字文档

博主介绍:✌全网粉丝3W,csdn特邀作者、CSDN新星计划导师、Java领域优质创作者,掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和学生毕业项目实战,高校老师/讲师/同行前辈交流✌ 技术范围:SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、…...

保姆级教程:手把手教你搞定OpenPnP主次基准点矫正(附PCB制作与避坑心得)

OpenPnP主次基准点矫正实战指南:从硬件准备到精准调试 1. 准备工作:构建稳定的校准环境 在开始OpenPnP主次基准点矫正之前,充分的准备工作能避免80%的常见问题。首先需要理解基准点在贴片机坐标系中的核心作用——它们如同地图上的经纬度&…...

别再死循环了!手把手教你用Python实现D*算法(附完整代码与避坑指南)

从理论到实践:Python实现D*算法的工程化指南与避坑策略 路径规划中的动态适应挑战 在机器人导航和游戏AI开发中,路径规划算法扮演着至关重要的角色。传统算法如A*和Dijkstra虽然能有效解决静态环境下的路径规划问题,但在动态变化的环境中却显…...

从2023蓝桥杯JavaB组省赛真题看算法思维与实战技巧

1. 从真题看算法思维培养 去年参加蓝桥杯省赛时,我对着那道阶乘求和题足足愣了十分钟。题目要求计算1!到202320232023!的和的最后9位数字,我第一反应就是暴力计算每个阶乘值再累加。结果刚算到20!就发现long类型溢出了,当时整个人都懵了。后来…...

使用Taotoken后如何通过用量看板清晰掌握各模型API消耗情况

🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 使用Taotoken后如何通过用量看板清晰掌握各模型API消耗情况 当你将多个大模型API的调用统一接入到Taotoken平台后,一个…...

从账单明细看 Taotoken 按 Token 计费模式带来的成本控制优势

🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 从账单明细看 Taotoken 按 Token 计费模式带来的成本控制优势 1. 成本感知的起点:账单明细结构 对于使用大模型 API 的…...

如何将 Infinix 手机中的联系人传输到 iPhone

如果您刚从Infinix Android手机换到新款 iPhone ,首先可能会担心如何安全快捷地将联系人从 Infinix 转移到 iPhone。由于这两个系统使用不同的数据生态系统,许多用户不确定哪种方法最有效。幸运的是,有几种可靠的方法可以转移您的通讯录&…...

UE5动画进阶:用Control Rig的Aim节点,5分钟搞定角色头部平滑跟随任意Actor

UE5动画进阶:Control Rig的Aim节点实现角色头部动态跟随 在游戏开发中,角色与环境的动态交互是提升沉浸感的关键要素之一。想象一个场景:NPC能够自然地跟随玩家的移动而转动头部,或是怪物精准锁定目标时的头部动作——这些细节往往…...

HLS行为差异测试:挑战与LLM驱动的解决方案

1. 高层次综合(HLS)行为差异测试的挑战与机遇在AI计算和边缘计算快速发展的今天,FPGA因其可重构性和并行计算能力,成为硬件加速的重要选择。高层次综合(High-Level Synthesis, HLS)技术允许开发者使用C/C等高级语言编写算法,然后自动转换为硬…...

7天掌握FontForge:免费开源字体编辑器的完整使用指南

7天掌握FontForge:免费开源字体编辑器的完整使用指南 【免费下载链接】fontforge Free (libre) font editor for Windows, Mac OS X and GNULinux 项目地址: https://gitcode.com/gh_mirrors/fo/fontforge 你是否曾梦想设计属于自己的字体?无论是…...

Claude Code 上下文管理机制深度拆解:超长 Agent 任务如何不崩盘

在一个真正复杂的企业级软件设计与编码任务里,Coding Agent 面对的从来不是一句简单的“帮我写个小游戏”。 它要理解用户的原始需求,要读取项目里的既有代码,要遵守架构约束、编码规范、接口协议,还要调用各种工具、加载不同的技能和规则,甚至记住用户十分钟前随口补充的…...

3分钟快速上手:Tsukimi打造你的个人Jellyfin媒体中心

3分钟快速上手:Tsukimi打造你的个人Jellyfin媒体中心 【免费下载链接】tsukimi A simple third-party Jellyfin client for Linux 项目地址: https://gitcode.com/gh_mirrors/ts/tsukimi 还在为复杂的媒体播放器设置而烦恼吗?Tsukimi这款简单易用…...

用Python+OpenCV+SORT搞定高空抛物监测:从摄像头选型到代码调试的保姆级避坑指南

PythonOpenCVSORT高空抛物监测系统实战:从硬件选型到算法调优全解析 1. 项目背景与技术选型 高空抛物监测系统作为智慧社区建设的关键环节,面临着复杂的环境挑战。传统监控摄像头仅能记录画面,无法实现主动预警。而基于计算机视觉的智能分析…...

别再死记硬背了!用打王者荣耀掉帧的例子,5分钟搞懂视频编码里的I/P/B帧

游戏卡顿背后的秘密:用王者荣耀掉帧理解视频编码中的I/P/B帧 当你正沉浸在王者荣耀的激烈团战中,手指在屏幕上飞速滑动,准备释放关键技能时,画面突然卡顿——右上角的FPS数值从60骤降到20。这种令人抓狂的体验背后,隐藏…...

4种颠覆性组合:重构Pixelle-Video的模块化潜能

4种颠覆性组合:重构Pixelle-Video的模块化潜能 【免费下载链接】Pixelle-Video 🚀 AI 全自动短视频引擎 | AI Fully Automated Short Video Engine 项目地址: https://gitcode.com/GitHub_Trending/pi/Pixelle-Video 想象一下:输入&qu…...