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()和onDestory()生命周期回调,根据需要重写对应的回调方法。InputMethodExtensionAbility的生命周期如下: 14 15- **onCreate** 16 服务被首次创建时触发该回调,开发者可以在此进行一些初始化的操作,例如注册公共事件监听等。 17 18 > **说明:** 19 > 20 > 如果服务已创建,再次启动该InputMethodExtensionAbility不会触发onCreate()回调。 21 22- **onDestroy** 23 当不再使用服务且准备将该实例销毁时,触发该回调。开发者可以在该回调中清理资源,如注销监听等。 24 25 26## 开发步骤 27 28开发者在实现一个输入法应用时,需要在DevEco Studio工程中新建一个InputMethodExtensionAbility,具体步骤如下: 29 30在工程Module对应的ets目录下,右键选择“New > Extention Ability > InputMethod”,即可创建出InputMethodExtensionAbility的最小化模板。 31 32> **说明:** 33> 在编译输入法应用时,要使用system_basic级别的签名,否则无法拉起输入法键盘。 34> [签名指导](https://developer.harmonyos.com/cn/docs/documentation/doc-guides/ohos-auto-configuring-signature-information-0000001271659465) 35 36最小化模板为一个最基本的输入法应用,包含软键盘拉起以及输入删除功能。后续开发者可在此基础上添加功能,如隐藏键盘等,实现自己的输入法应用。 37 38最小化模板主要包含四个文件,分别为KeyboardController.ts、InputMethodService.ts、Index.ets以及KeyboardKeyData.ts。目录如下: 39 40``` 41/src/main/ 42├── ets/inputmethodextability 43│ └──model/KeyboardController.ts # 显示键盘 44│ └──InputMethodService.ts # 自定义类继承InputMethodExtensionAbility并加上需要的生命周期回调 45│ └──pages 46│ └── Index.ets # 绘制键盘,添加输入删除功能 47│ └── KeyboardKeyData.ts # 键盘属性定义 48├── resources/base/profile/main_pages.json 49``` 50 51## 文件介绍 52 531. InputMethodService.ts文件。 54 55 在InputMethodService.ts文件中,增加导入InputMethodExtensionAbility的依赖包,自定义类继承InputMethodExtensionAbility并加上需要的生命周期回调。 56 57 ```ts 58 import InputMethodExtensionAbility from '@ohos.InputMethodExtensionAbility'; 59 import { KeyboardController } from './model/KeyboardController' 60 61 export default class InputDemoService extends InputMethodExtensionAbility { 62 private keyboardController: KeyboardController; 63 64 onCreate(want) { 65 this.keyboardController = new KeyboardController(this.context); 66 this.keyboardController.onCreate(); // 初始化窗口并注册对输入法框架的事件监听 67 } 68 69 onDestroy() { 70 console.log("onDestroy."); 71 this.keyboardController.onDestroy(); // 销毁窗口并去注册事件监听 72 } 73 } 74 ``` 75 762. KeyboardController.ts文件。 77 78 ```ts 79 import inputMethodEngine from '@ohos.inputMethodEngine'; 80 import display from '@ohos.display'; 81 import windowManager from '@ohos.window'; 82 83 // 调用输入法框架的getInputMethodAbility方法获取实例,并由此实例调用输入法框架功能接口 84 globalThis.inputAbility = inputMethodEngine.getInputMethodAbility(); 85 86 export class KeyboardController { 87 mContext; // 保存InputMethodExtensionAbility中的context属性 88 WINDOW_TYPE_INPUT_METHOD_FLOAT = 2105; // 定义窗口类型,2105代表输入法窗口类型,用于创建输入法应用窗口 89 windowName = 'inputApp'; 90 private windowHeight: number = 0; 91 private windowWidth: number = 0; 92 private nonBarPosition: number = 0; 93 private isWindowShowing: boolean = false; 94 95 constructor(context) { 96 this.mContext = context; 97 } 98 99 public onCreate(): void 100 { 101 this.initWindow(); // 初始化窗口 102 this.registerListener(); // 注册对输入法框架的事件监听 103 } 104 105 public onDestroy(): void // 应用生命周期销毁 106 { 107 this.unRegisterListener(); // 去注册事件监听 108 let win = windowManager.findWindow(this.windowName); 109 win.destroyWindow(); // 销毁窗口 110 } 111 112 private initWindow(): void // 初始化窗口 113 { 114 let dis = display.getDefaultDisplaySync(); 115 let dWidth = dis.width; 116 let dHeight = dis.height; 117 let keyHeightRate = 0.47; 118 let keyHeight = dHeight * keyHeightRate; 119 this.windowWidth = dWidth; 120 this.windowHeight = keyHeight; 121 this.nonBarPosition = dHeight - keyHeight; 122 123 let config = { 124 name: this.windowName, 125 windowType: this.WINDOW_TYPE_INPUT_METHOD_FLOAT, 126 ctx: this.mContext 127 } 128 windowManager.createWindow(config).then((win) => { // 根据窗口类型创建窗口 129 win.resize(dWidth, keyHeight).then(() => { 130 win.moveWindowTo(0, this.nonBarPosition).then(() => { 131 win.setUIContent('pages/InputMethodExtAbility/Index').then(() => { 132 }); 133 }); 134 }); 135 }); 136 } 137 138 private registerListener(): void 139 { 140 this.registerInputListener(); // 注册对输入法框架服务的监听 141 globalThis.inputAbility.on('keyboardShow', () => { // 注册显示键盘事件监听 142 if (this.isWindowShowing) { 143 return; 144 } 145 this.isWindowShowing = true; 146 this.showHighWindow(); // 显示窗口 147 }); 148 ... 149 // 注册隐藏键盘事件监听等 150 } 151 152 private registerInputListener() { // 注册对输入法框架服务的开启及停止事件监听 153 globalThis.inputAbility.on('inputStart', (kbController, textInputClient) => { 154 globalThis.textInputClient = textInputClient; // 此为输入法客户端实例,由此调用输入法框架提供给输入法应用的功能接口 155 globalThis.keyboardController = kbController; 156 }) 157 globalThis.inputAbility.on('inputStop', (imeId) => { 158 if (imeId == "包名/Ability名") { 159 this.mContext.destroy(); // 销毁InputMethodExtensionAbility服务 160 } 161 }); 162 } 163 164 private unRegisterListener(): void 165 { 166 globalThis.inputAbility.off('inputStart'); 167 globalThis.inputAbility.off('inputStop', () => {}); 168 globalThis.inputAbility.off('keyboardShow'); 169 } 170 171 private showHighWindow() { 172 let win = windowManager.findWindow(this.windowName) 173 win.resize(this.windowWidth, this.windowHeight).then(() => { 174 win.moveWindowTo(0, this.nonBarPosition).then(() => { 175 win.showWindow().then(() => { 176 this.isWindowShowing = false; 177 }) 178 }) 179 }) 180 } 181 } 182 ``` 183 1843. KeyboardKeyData.ts文件。 185 186 定义软键盘的按键显示内容。 187 188 ```ts 189 export interface sourceListType { 190 content: string, 191 } 192 193 export let numberSourceListData: sourceListType[] = [ 194 { 195 content: '1' 196 }, 197 { 198 content: '2' 199 }, 200 { 201 content: '3' 202 }, 203 { 204 content: '4' 205 }, 206 { 207 content: '5' 208 }, 209 { 210 content: '6' 211 }, 212 { 213 content: '7' 214 }, 215 { 216 content: '8' 217 }, 218 { 219 content: '9' 220 }, 221 { 222 content: '0' 223 } 224 ] 225 ``` 226 2274. Index.ets文件。 228 229 主要描绘了具体按键功能。如按下数字键,就会将数字内容在输入框中打印出来,按下删除键,就会将内容删除。 230 231 同时在resources/base/profile/main_pages.json文件的src字段中添加此文件路径。 232 233 ```ets 234 import { numberSourceListData, sourceListType } from './keyboardKeyData' 235 236 @Component 237 struct keyItem { 238 private keyValue: sourceListType 239 @State keyBgc: string = "#fff" 240 @State keyFontColor: string = "#000" 241 242 build() { 243 Column() { 244 Flex({ direction: FlexDirection.Column, 245 alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { 246 Text(this.keyValue.content).fontSize(20).fontColor(this.keyFontColor) 247 } 248 } 249 .backgroundColor(this.keyBgc) 250 .borderRadius(6) 251 .width("8%") 252 .height("65%") 253 .onTouch((event: TouchEvent) => { 254 if (event.type === TouchType.Down) { 255 globalThis.textInputClient.insertText(this.keyValue.content); 256 } 257 }) 258 } 259 } 260 261 // 删除组件 262 @Component 263 export struct deleteItem { 264 @State keyBgc: string = "#fff" 265 @State keyFontColor: string = "#000" 266 267 build() { 268 Column() { 269 Flex({ direction: FlexDirection.Column, 270 alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { 271 Text("删除").fontSize(20).fontColor(this.keyFontColor) 272 } 273 } 274 .backgroundColor(this.keyBgc) 275 .width("13%") 276 .borderRadius(6) 277 .onTouch((event: TouchEvent) => { 278 if (event.type === TouchType.Down) { 279 globalThis.textInputClient.deleteForward(1); 280 } 281 }) 282 } 283 } 284 285 // 数字键盘 286 @Component 287 struct numberMenu { 288 private numberList: sourceListType[] 289 290 build() { 291 Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.SpaceEvenly }) { 292 Flex({ justifyContent: FlexAlign.SpaceBetween }) { 293 ForEach(this.numberList, (item: sourceListType) => { // 数字键盘第一行 294 keyItem({ keyValue: item }) 295 }, (item: sourceListType) => item.content); 296 } 297 .padding({ top: "2%" }) 298 .width("96%") 299 .height("25%") 300 301 Flex({ justifyContent: FlexAlign.SpaceBetween }) { 302 deleteItem() 303 } 304 .width("96%") 305 .height("25%") 306 } 307 } 308 } 309 310 @Entry 311 @Component 312 struct Index { 313 private numberList: sourceListType[] = numberSourceListData 314 315 build() { 316 Stack() { 317 Flex({ 318 direction: FlexDirection.Column, 319 alignItems: ItemAlign.Center, 320 justifyContent: FlexAlign.End 321 }) { 322 Flex({ 323 direction: FlexDirection.Column, 324 alignItems: ItemAlign.Center, 325 justifyContent: FlexAlign.SpaceBetween 326 }) { 327 numberMenu({ 328 numberList: this.numberList 329 }) 330 } 331 .align(Alignment.End) 332 .width("100%") 333 .height("75%") 334 } 335 .height("100%").align(Alignment.End).backgroundColor("#cdd0d7") 336 } 337 .position({ x: 0, y: 0 }).zIndex(99999) 338 } 339 } 340 ``` 341 3425. 在工程Module对应的[module.json5配置文件](../quick-start/module-configuration-file.md)中注册InputMethodExtensionAbility,type标签需要设置为“inputMethod”,srcEntry标签表示当前InputMethodExtensionAbility组件所对应的代码路径。 343 344 ```ts 345 { 346 "module": { 347 ... 348 "extensionAbilities": [ 349 { 350 "description": "inputMethod", 351 "icon": "$media:icon", 352 "name": "InputMethodExtAbility", 353 "srcEntry": "./ets/inputmethodextability/InputMethodService.ts", 354 "type": "inputMethod", 355 "exported": true, 356 } 357 ] 358 } 359 } 360 ``` 361 362 363