1# InputMethodExtensionAbility 2 3## 使用场景 4[InputMethodExtensionAbility](../reference/apis/js-apis-inputmethod-extension-ability.md)基于[ExtensionAbility](extensionability-overview.md)框架,用于开发输入法应用。 5 6[InputMethodExtensionAbility](../reference/apis/js-apis-inputmethod-extension-ability.md)实例及其所在的ExtensionAbility进程的整个生命周期,都是由输入法框架进行调度管理。输入法框架提供了[InputMethodExtensionAbility](../reference/apis/js-apis-inputmethod-extension-ability.md)基类,开发者需要派生此基类,以实现输入法应用生命周期开始和销毁时的相关初始化操作和资源清理工作等。 7 8[InputMethodExtensionAbility](../reference/apis/js-apis-inputmethod-extension-ability.md)通过[InputMethodExtensionContext](../reference/apis/js-apis-inputmethod-extension-context.md)提供相关能力。 9 10 11## 实现一个输入法应用 12 13[InputMethodExtensionAbility](../reference/apis/js-apis-inputmethod-extension-ability.md)提供了onCreate()和onDestroy()生命周期回调,根据需要重写对应的回调方法。InputMethodExtensionAbility的生命周期如下: 14 15- **onCreate()** 16 17 服务被首次创建时触发该回调,开发者可以在此进行一些初始化的操作,例如注册公共事件监听等。 18 19 > **说明:** 20 > 21 > 如果服务已创建,再次启动该InputMethodExtensionAbility不会触发onCreate()回调。 22 23- **onDestroy()** 24 25 当不再使用服务且准备将该实例销毁时,触发该回调。开发者可以在该回调中清理资源,如注销监听等。 26 27 28## 开发步骤 29 30开发者在实现一个输入法应用时,需要在DevEco Studio工程中新建一个InputMethodExtensionAbility,具体步骤如下: 31 321. 在工程Module对应的ets目录下,右键选择“New > Directory”,新建一个目录,并命名为InputMethodExtensionAbility。 33 342. 在InputMethodExtensionAbility目录下,右键选择“New > File”,新建四个文件,分别为KeyboardController.ts、InputMethodService.ts、Index.ets以及KeyboardKeyData.ts。目录如下: 35 36``` 37/src/main/ 38├── ets/inputmethodextability 39│ └──model/KeyboardController.ts # 显示键盘 40│ └──InputMethodService.ts # 自定义类继承InputMethodExtensionAbility并加上需要的生命周期回调 41│ └──pages 42│ └── Index.ets # 绘制键盘,添加输入删除功能 43│ └── KeyboardKeyData.ts # 键盘属性定义 44├── resources/base/profile/main_pages.json 45``` 46 47## 文件介绍 48 491. InputMethodService.ts文件。 50 51 在InputMethodService.ts文件中,增加导入InputMethodExtensionAbility的依赖包,自定义类继承InputMethodExtensionAbility并加上需要的生命周期回调。 52 53 ```ts 54 import Want from '@ohos.app.ability.Want'; 55 import InputMethodExtensionAbility from '@ohos.InputMethodExtensionAbility'; 56 import keyboardController from './model/KeyboardController' 57 58 export default class InputDemoService extends InputMethodExtensionAbility { 59 60 onCreate(want: Want): void { 61 keyboardController.onCreate(this.context); // 初始化窗口并注册对输入法框架的事件监听 62 } 63 64 onDestroy(): void { 65 console.log("onDestroy."); 66 keyboardController.onDestroy(); // 销毁窗口并去注册事件监听 67 } 68 } 69 ``` 70 712. KeyboardController.ts文件。 72 73 ```ts 74 import common from '@ohos.app.ability.common'; 75 import display from '@ohos.display'; 76 import inputMethodEngine from '@ohos.inputMethodEngine'; 77 import InputMethodExtensionContext from '@ohos.InputMethodExtensionContext'; 78 79 // 调用输入法框架的getInputMethodAbility方法获取实例,并由此实例调用输入法框架功能接口 80 const inputMethodAbility: inputMethodEngine.InputMethodAbility = inputMethodEngine.getInputMethodAbility(); 81 82 export class KeyboardController { 83 private mContext: InputMethodExtensionContext | undefined = undefined; // 保存InputMethodExtensionAbility中的context属性 84 private panel: inputMethodEngine.Panel | undefined = undefined; 85 private textInputClient: inputMethodEngine.InputClient | undefined = undefined; 86 private keyboardController: inputMethodEngine.KeyboardController | undefined = undefined; 87 88 constructor() { 89 } 90 91 public onCreate(context: InputMethodExtensionContext): void 92 { 93 this.mContext = context; 94 this.initWindow(); // 初始化窗口 95 this.registerListener(); // 注册对输入法框架的事件监听 96 } 97 98 public onDestroy(): void // 应用生命周期销毁 99 { 100 this.unRegisterListener(); // 去注册事件监听 101 if(this.panel) { // 销毁窗口 102 this.panel.hide(); 103 inputMethodAbility.destroyPanel(this.panel); 104 } 105 if(this.mContext) { 106 this.mContext.destroy(); 107 } 108 } 109 110 public insertText(text: string): void { 111 if(this.textInputClient) { 112 this.textInputClient.insertText(text); 113 } 114 } 115 116 public deleteForward(length: number): void { 117 if(this.textInputClient) { 118 this.textInputClient.deleteForward(length); 119 } 120 } 121 122 private initWindow(): void // 初始化窗口 123 { 124 if(this.mContext === undefined) { 125 return; 126 } 127 let dis = display.getDefaultDisplaySync(); 128 let dWidth = dis.width; 129 let dHeight = dis.height; 130 let keyHeightRate = 0.47; 131 let keyHeight = dHeight * keyHeightRate; 132 let nonBarPosition = dHeight - keyHeight; 133 let panelInfo: inputMethodEngine.PanelInfo = { 134 type: inputMethodEngine.PanelType.SOFT_KEYBOARD, 135 flag: inputMethodEngine.PanelFlag.FLG_FIXED 136 }; 137 inputMethodAbility.createPanel(this.mContext, panelInfo).then(async (inputPanel: inputMethodEngine.Panel) => { 138 this.panel = inputPanel; 139 if(this.panel) { 140 await this.panel.resize(dWidth, keyHeight); 141 await this.panel.moveTo(0, nonBarPosition); 142 await this.panel.setUiContent('inputmethodextability/pages/Index'); 143 } 144 }); 145 } 146 147 private registerListener(): void 148 { 149 this.registerInputListener(); // 注册对输入法框架服务的监听 150 ... 151 // 注册隐藏键盘事件监听等 152 } 153 154 private registerInputListener(): void { // 注册对输入法框架服务的开启及停止事件监听 155 inputMethodAbility.on('inputStart', (kbController, textInputClient) => { 156 this.textInputClient = textInputClient; // 此为输入法客户端实例,由此调用输入法框架提供给输入法应用的功能接口 157 this.keyboardController = kbController; 158 }) 159 inputMethodAbility.on('inputStop', () => { 160 this.onDestroy(); // 销毁KeyboardController 161 }); 162 } 163 164 private unRegisterListener(): void 165 { 166 inputMethodAbility.off('inputStart'); 167 inputMethodAbility.off('inputStop', () => {}); 168 } 169 } 170 171 const keyboardController = new KeyboardController(); 172 173 export default keyboardController; 174 ``` 175 1763. KeyboardKeyData.ts文件。 177 178 定义软键盘的按键显示内容。 179 180 ```ts 181 export interface sourceListType { 182 content: string, 183 } 184 185 export let numberSourceListData: sourceListType[] = [ 186 { 187 content: '1' 188 }, 189 { 190 content: '2' 191 }, 192 { 193 content: '3' 194 }, 195 { 196 content: '4' 197 }, 198 { 199 content: '5' 200 }, 201 { 202 content: '6' 203 }, 204 { 205 content: '7' 206 }, 207 { 208 content: '8' 209 }, 210 { 211 content: '9' 212 }, 213 { 214 content: '0' 215 } 216 ] 217 ``` 218 2194. Index.ets文件。 220 221 主要描绘了具体按键功能。如按下数字键,就会将数字内容在输入框中打印出来,按下删除键,就会将内容删除。 222 223 同时在resources/base/profile/main_pages.json文件的src字段中添加此文件路径。 224 225 ```ets 226 import { numberSourceListData, sourceListType } from './keyboardKeyData'; 227 import keyboardController from '../model/KeyboardController'; 228 229 @Component 230 struct keyItem { 231 private keyValue: sourceListType = numberSourceListData[0]; 232 @State keyBgc: string = "#fff" 233 @State keyFontColor: string = "#000" 234 235 build() { 236 Column() { 237 Flex({ direction: FlexDirection.Column, 238 alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { 239 Text(this.keyValue.content).fontSize(20).fontColor(this.keyFontColor) 240 } 241 } 242 .backgroundColor(this.keyBgc) 243 .borderRadius(6) 244 .width("8%") 245 .height("65%") 246 .onClick(() => { 247 keyboardController.insertText(this.keyValue.content); 248 }) 249 } 250 } 251 252 // 删除组件 253 @Component 254 export struct deleteItem { 255 @State keyBgc: string = "#fff" 256 @State keyFontColor: string = "#000" 257 258 build() { 259 Column() { 260 Flex({ direction: FlexDirection.Column, 261 alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { 262 Text("删除").fontSize(20).fontColor(this.keyFontColor) 263 } 264 } 265 .backgroundColor(this.keyBgc) 266 .width("13%") 267 .borderRadius(6) 268 .onClick(() => { 269 keyboardController.deleteForward(1); 270 }) 271 } 272 } 273 274 // 数字键盘 275 @Component 276 struct numberMenu { 277 private numberList: sourceListType[] = numberSourceListData; 278 279 build() { 280 Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.SpaceEvenly }) { 281 Flex({ justifyContent: FlexAlign.SpaceBetween }) { 282 ForEach(this.numberList, (item: sourceListType) => { // 数字键盘第一行 283 keyItem({ keyValue: item }) 284 }, (item: sourceListType) => item.content); 285 } 286 .padding({ top: "2%" }) 287 .width("96%") 288 .height("25%") 289 290 Flex({ justifyContent: FlexAlign.SpaceBetween }) { 291 deleteItem() 292 } 293 .width("96%") 294 .height("25%") 295 } 296 } 297 } 298 299 @Entry 300 @Component 301 struct Index { 302 private numberList: sourceListType[] = numberSourceListData 303 304 build() { 305 Stack() { 306 Flex({ 307 direction: FlexDirection.Column, 308 alignItems: ItemAlign.Center, 309 justifyContent: FlexAlign.End 310 }) { 311 Flex({ 312 direction: FlexDirection.Column, 313 alignItems: ItemAlign.Center, 314 justifyContent: FlexAlign.SpaceBetween 315 }) { 316 numberMenu({ 317 numberList: this.numberList 318 }) 319 } 320 .align(Alignment.End) 321 .width("100%") 322 .height("75%") 323 } 324 .height("100%").align(Alignment.End).backgroundColor("#cdd0d7") 325 } 326 .position({ x: 0, y: 0 }).zIndex(99999) 327 } 328 } 329 ``` 330 3315. 在工程Module对应的[module.json5配置文件](../quick-start/module-configuration-file.md)中注册InputMethodExtensionAbility,type标签需要设置为“inputMethod”,srcEntry标签表示当前InputMethodExtensionAbility组件所对应的代码路径。 332 333 ```json 334 { 335 "module": { 336 ... 337 "extensionAbilities": [ 338 { 339 "description": "inputMethod", 340 "icon": "$media:icon", 341 "name": "InputMethodExtAbility", 342 "srcEntry": "./ets/inputmethodextability/InputMethodService.ts", 343 "type": "inputMethod", 344 "exported": true, 345 } 346 ] 347 } 348 } 349 ``` 350 351## 验证方法 352 3531. 使用hdc命令,拉起选择输入法弹窗应用:`hdc shell aa start ability -a InputMethod -b cn.openharmonyinputmethodchoosedialog` 354 3552. 在弹窗上显示的输入法应用列表中,选择并点击demo应用,将demo应用切换为当前输入法。 356 3573. 点击任意编辑框,即可拉起输入法demo。 358 359## 约束与限制 360 361为了降低InputMethodExtensionAbility能力被三方应用滥用的风险,在InputMethodExtensionAbility中限制调用以下模块中的接口。 362 363> **说明:** 364> 365> - 若导入被限制的模块,在编译时不报错,在运行时会返回错误的值,即undefined,导致不生效。 366> - 当前未禁止对音频管理模块[@ohos.multimedia.audio (音频管理)](../reference/apis/js-apis-audio.md)的访问,但要求开发者应遵循以下约定: 367> - 不得因用户未授予录音权限而禁止用户使用输入法应用的非语音输入法功能; 368> - 仅允许InputMethodExtensionAbility处于前台时开展与录音相关的业务。如仅允许软键盘在前台且用户主动操作语音输入法时,才进行录音;应用切换到后台时,应主动停止录音; 369> - 系统会逐步增加对违反以上约定的行为进行管控和识别,因此未遵守此约定可能会造成业务功能异常。 370 371**模块列表:** 372 373- [@ohos.ability.featureAbility (FeatureAbility模块)](../reference/apis/js-apis-ability-featureAbility.md) 374- [@ohos.ability.particleAbility (ParticleAbility模块)](../reference/apis/js-apis-ability-particleAbility.md) 375- [@ohos.account.distributedAccount (分布式帐号管理)](../reference/apis/js-apis-distributed-account.md) 376- [@ohos.backgroundTaskManager (后台任务管理)](../reference/apis/js-apis-backgroundTaskManager.md) 377- [@ohos.bluetooth (蓝牙)](../reference/apis/js-apis-bluetooth.md) 378- [@ohos.bluetoothManager (蓝牙)](../reference/apis/js-apis-bluetoothManager.md) 379- [@ohos.connectedTag (有源标签)](../reference/apis/js-apis-connectedTag.md) 380- [@ohos.geolocation (位置服务)](../reference/apis/js-apis-geolocation.md) 381- [@ohos.geoLocationManager (位置服务)](../reference/apis/js-apis-geoLocationManager.md) 382- [@ohos.nfc.cardEmulation (标准NFC-cardEmulation)](../reference/apis/js-apis-cardEmulation.md) 383- [@ohos.nfc.controller (标准NFC)](../reference/apis/js-apis-nfcController.md) 384- [@ohos.nfc.tag (标准NFC-Tag)](../reference/apis/js-apis-nfcTag.md) 385- [@ohos.reminderAgent (后台代理提醒)](../reference/apis/js-apis-reminderAgent.md) 386- [@ohos.reminderAgentManager (后台代理提醒)](../reference/apis/js-apis-reminderAgentManager.md) 387- [@ohos.sensor (传感器)](../reference/apis/js-apis-sensor.md) 388- [@ohos.telephony.call (拨打电话)](../reference/apis/js-apis-call.md) 389- [@ohos.telephony.data (蜂窝数据)](../reference/apis/js-apis-telephony-data.md) 390- [@ohos.telephony.observer (observer)](../reference/apis/js-apis-observer.md) 391- [@ohos.telephony.radio (网络搜索)](../reference/apis/js-apis-radio.md) 392- [@ohos.telephony.sim (SIM卡管理)](../reference/apis/js-apis-sim.md) 393- [@ohos.telephony.sms (短信服务)](../reference/apis/js-apis-sms.md) 394- [@ohos.wallpaper (壁纸)](../reference/apis/js-apis-wallpaper.md) 395- [@ohos.wifiext (WLAN扩展接口)](../reference/apis/js-apis-wifiext.md) 396- [@ohos.wifiManager (WLAN)](../reference/apis/js-apis-wifiManager.md) 397- [@ohos.wifiManagerExt (WLAN扩展接口)](../reference/apis/js-apis-wifiManagerExt.md) 398- [@system.geolocation (地理位置)](../reference/apis/js-apis-system-location.md) 399- [nfctech (标准NFC-Tag Nfc 技术)](../reference/apis/js-apis-nfctech.md) 400- [tagSession (标准NFC-Tag TagSession)](../reference/apis/js-apis-tagSession.md) 401 402 403 404## 相关实例 405 406针对InputMethodExtensionAbility开发,有以下相关实例可供参考: 407 408- [Kika输入法](https://gitee.com/openharmony/applications_app_samples/tree/OpenHarmony-4.0-Release/code/Solutions/InputMethod/KikaInput)