• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 实现一个输入法应用
2<!--Kit: IME Kit-->
3<!--Subsystem: MiscServices-->
4<!--Owner: @illybyy-->
5<!--Designer: @andeszhang-->
6<!--Tester: @murphy1984-->
7<!--Adviser: @zhang_yixin13-->
8
9[InputMethodExtensionAbility](../reference/apis-ime-kit/js-apis-inputmethod-extension-ability.md)提供了onCreate()和onDestroy()生命周期回调,根据需要重写对应的回调方法。InputMethodExtensionAbility的生命周期如下:
10
11- **onCreate()**
12
13  服务被首次创建时触发该回调,开发者可以在此进行一些初始化的操作,例如注册公共事件监听等。
14
15  > **说明:**
16  >
17  > 如果服务已创建,再次启动该InputMethodExtensionAbility不会触发onCreate()回调。
18
19- **onDestroy()**
20
21  当不再使用服务且准备将该实例销毁时,触发该回调。开发者可以在该回调中清理资源,如注销监听等。
22
23
24## 开发步骤
25
26开发者在实现一个输入法应用时,需要在DevEco Studio工程中新建一个InputMethodExtensionAbility,具体步骤如下:
27
281. 在工程Module对应的ets目录下,右键选择“New > Directory”,新建一个目录,并命名为InputMethodExtensionAbility。
29
302. 在InputMethodExtensionAbility目录下,右键选择“New > File”,新建四个文件,分别为KeyboardController.tsInputMethodService.tsIndex.ets以及KeyboardKeyData.ts。目录如下:
31
32```
33/src/main/
34├── ets/InputMethodExtensionAbility
35│       └──model/KeyboardController.ts			# 显示键盘
36│       └──InputMethodService.ts				# 自定义类继承InputMethodExtensionAbility并加上需要的生命周期回调
37│       └──pages
38│         └── Index.ets						# 绘制键盘,添加输入删除功能
39│         └── KeyboardKeyData.ts			    # 键盘属性定义
40├── resources/base/profile/main_pages.json
41```
42
43## 文件介绍
44
451. InputMethodService.ts文件。
46
47InputMethodService.ts文件中,增加导入InputMethodExtensionAbility的依赖包,自定义类继承InputMethodExtensionAbility并加上需要的生命周期回调。
48
49   ```ts
50   import { Want } from '@kit.AbilityKit';
51   import keyboardController from './model/KeyboardController';
52   import { InputMethodExtensionAbility } from '@kit.IMEKit';
53
54   export default class InputDemoService extends InputMethodExtensionAbility {
55
56     onCreate(want: Want): void {
57       keyboardController.onCreate(this.context); // 初始化窗口并注册对输入法框架的事件监听
58     }
59
60     onDestroy(): void {
61       console.info("onDestroy.");
62       keyboardController.onDestroy(); // 销毁窗口并去注册事件监听
63     }
64   }
65   ```
66
67<!--RP2-->
682. KeyboardController.ts文件。
69
70   ```ts
71   import { display } from '@kit.ArkUI';
72   import { inputMethodEngine, InputMethodExtensionContext } from '@kit.IMEKit';
73   import { BusinessError } from '@kit.BasicServicesKit';
74
75   // 调用输入法框架的getInputMethodAbility方法获取实例,并由此实例调用输入法框架功能接口
76   const inputMethodAbility: inputMethodEngine.InputMethodAbility = inputMethodEngine.getInputMethodAbility();
77
78   export class KeyboardController {
79     private mContext: InputMethodExtensionContext | undefined = undefined; // 保存InputMethodExtensionAbility中的context属性
80     private panel: inputMethodEngine.Panel | undefined = undefined;
81     private textInputClient: inputMethodEngine.InputClient | undefined = undefined;
82     private keyboardController: inputMethodEngine.KeyboardController | undefined = undefined;
83
84     constructor() {
85     }
86
87     public onCreate(context: InputMethodExtensionContext): void
88     {
89       this.mContext = context;
90       this.initWindow(); // 初始化窗口
91       this.registerListener(); // 注册对输入法框架的事件监听
92     }
93
94     public onDestroy(): void // 应用生命周期销毁
95     {
96       this.unRegisterListener(); // 去注册事件监听
97       if(this.panel) { // 销毁窗口
98         inputMethodAbility.destroyPanel(this.panel);
99       }
100       if(this.mContext) {
101         this.mContext.destroy();
102       }
103     }
104
105     public insertText(text: string): void {
106       if(this.textInputClient) {
107         this.textInputClient.insertText(text);
108       }
109     }
110
111     public deleteForward(length: number): void {
112       if(this.textInputClient) {
113         this.textInputClient.deleteForward(length);
114       }
115     }
116
117     private initWindow(): void // 初始化窗口
118     {
119       if(this.mContext === undefined) {
120         return;
121       }
122       let dis = display.getDefaultDisplaySync();
123       let dWidth = dis.width;
124       let dHeight = dis.height;
125       let keyHeightRate = 0.47;
126       let keyHeight = dHeight * keyHeightRate;
127       let nonBarPosition = dHeight - keyHeight;
128       let panelInfo: inputMethodEngine.PanelInfo = {
129         type: inputMethodEngine.PanelType.SOFT_KEYBOARD,
130         flag: inputMethodEngine.PanelFlag.FLG_FIXED
131       };
132       inputMethodAbility.createPanel(this.mContext, panelInfo).then(async (inputPanel: inputMethodEngine.Panel) => {
133         this.panel = inputPanel;
134         if (this.panel) {
135           await this.panel.resize(dWidth, keyHeight);
136           await this.panel.moveTo(0, nonBarPosition);
137           await this.panel.setUiContent('InputMethodExtensionAbility/pages/Index');
138         }
139       }).catch((err: BusinessError) => {
140         console.error(`Failed to createPanel, code: ${err.code}, message: ${err.message}`);
141       });
142     }
143
144     private registerListener(): void
145     {
146       this.registerInputListener(); // 注册对输入法框架服务的监听
147       // 注册隐藏键盘事件监听等
148     }
149
150     private registerInputListener(): void {
151       // 注册开始输入的事件监听
152       inputMethodAbility.on('inputStart', (kbController, textInputClient) => {
153         this.textInputClient = textInputClient; // 此为输入法客户端实例,由此调用输入法框架提供给输入法应用的功能接口
154         this.keyboardController = kbController;
155       })
156       inputMethodAbility.on('inputStop', this.inputStopCallback);
157     }
158
159     private inputStopCallback(): void {
160       this.onDestroy(); // 销毁KeyboardController
161     }
162
163     private unRegisterListener(): void {
164       inputMethodAbility.off('inputStart');
165       inputMethodAbility.off('inputStop', this.inputStopCallback);
166     }
167   }
168
169   const keyboardController = new KeyboardController();
170
171   export default keyboardController;
172   ```
173   <!--RP2End-->
1743. KeyboardKeyData.ts文件。
175
176   定义软键盘的按键显示内容。
177
178   ```ts
179   export interface sourceListType {
180     content: string,
181   }
182
183   export const numberSourceListData: sourceListType[] = [
184     {
185       content: '1'
186     },
187     {
188       content: '2'
189     },
190     {
191       content: '3'
192     },
193     {
194       content: '4'
195     },
196     {
197       content: '5'
198     },
199     {
200       content: '6'
201     },
202     {
203       content: '7'
204     },
205     {
206       content: '8'
207     },
208     {
209       content: '9'
210     },
211     {
212       content: '0'
213     }
214   ]
215   ```
216
2174. Index.ets文件。
218
219   主要描绘了具体按键功能。如按下数字键,就会将数字内容在输入框中打印出来,按下删除键,就会将内容删除。
220
221   <!--Del-->同时在resources/base/profile/main_pages.json文件的src字段中添加此文件路径。<!--DelEnd-->
222
223   ```ets
224   import { numberSourceListData, sourceListType } from './KeyboardKeyData';
225   import keyboardController from '../model/KeyboardController';
226
227   @Component
228   struct keyItem {
229     private keyValue: sourceListType = numberSourceListData[0];
230     @State keyBgc: string = "#fff"
231     @State keyFontColor: string = "#000"
232
233     build() {
234       Column() {
235         Flex({ direction: FlexDirection.Column,
236           alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
237           Text(this.keyValue.content).fontSize(20).fontColor(this.keyFontColor)
238         }
239       }
240       .backgroundColor(this.keyBgc)
241       .borderRadius(6)
242       .width("8%")
243       .height("65%")
244       .onClick(() => {
245         keyboardController.insertText(this.keyValue.content);
246       })
247     }
248   }
249
250   // 删除组件
251   @Component
252   export struct deleteItem {
253     @State keyBgc: string = "#fff"
254     @State keyFontColor: string = "#000"
255
256     build() {
257       Column() {
258         Flex({ direction: FlexDirection.Column,
259           alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
260           Text("删除").fontSize(20).fontColor(this.keyFontColor)
261         }
262       }
263       .backgroundColor(this.keyBgc)
264       .width("13%")
265       .borderRadius(6)
266       .onClick(() => {
267         keyboardController.deleteForward(1);
268       })
269     }
270   }
271
272   // 数字键盘
273   @Component
274   struct numberMenu {
275     private numberList: sourceListType[] = numberSourceListData;
276
277     build() {
278       Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.SpaceEvenly }) {
279         Flex({ justifyContent: FlexAlign.SpaceBetween }) {
280           ForEach(this.numberList, (item: sourceListType) => { // 数字键盘第一行
281             keyItem({ keyValue: item })
282           }, (item: sourceListType) => item.content);
283         }
284         .padding({ top: "2%" })
285         .width("96%")
286         .height("25%")
287
288         Flex({ justifyContent: FlexAlign.SpaceBetween }) {
289           deleteItem()
290         }
291         .width("96%")
292         .height("25%")
293       }
294     }
295   }
296
297   @Entry
298   @Component
299   struct Index {
300     private numberList: sourceListType[] = numberSourceListData
301
302     build() {
303       Stack() {
304         Flex({
305           direction: FlexDirection.Column,
306           alignItems: ItemAlign.Center,
307           justifyContent: FlexAlign.End
308         }) {
309               Flex({
310                 direction: FlexDirection.Column,
311                 alignItems: ItemAlign.Center,
312                 justifyContent: FlexAlign.SpaceBetween
313               }) {
314                 numberMenu({
315                   numberList: this.numberList
316                 })
317               }
318               .align(Alignment.End)
319               .width("100%")
320               .height("75%")
321             }
322         .height("100%").align(Alignment.End).backgroundColor("#cdd0d7")
323       }
324       .position({ x: 0, y: 0 }).zIndex(99999)
325     }
326   }
327   ```
328
329<!--Del-->
3305. 在工程Module对应的[module.json5配置文件](../quick-start/module-configuration-file.md)中注册InputMethodExtensionAbility,type标签需要设置为“inputMethod”,srcEntry标签表示当前InputMethodExtensionAbility组件所对应的代码路径。
331
332   ```json
333   {
334     "module": {
335       ...
336       "extensionAbilities": [
337         {
338           "description": "inputMethod",
339           "name": "InputMethodExtensionAbility",
340           "icon": "$media:app_icon",
341           "srcEntry": "./ets/InputMethodExtensionAbility/InputMethodService.ts",
342           "type": "inputMethod",
343           "exported": true
344         }
345       ]
346     }
347   }
348   ```
349<!--DelEnd-->
350
351
352<!--RP3-->
353
354<!--RP3End-->
355
356## 约束与限制
357
358为了降低InputMethodExtensionAbility能力被三方应用滥用的风险,现通过基础访问模式的功能约束对输入法应用进行安全管控。
359
360> **说明:**
361>
362> 严格遵从基础访问模式的功能约束。在此模式下,开发者应仅提供基础打字功能,不应提供任何形式与网络交互相关的功能。系统会逐步增加基础访问模式的安全管控能力,包括但不限于:以独立进程和沙箱的方式运行Extension进程;禁止Extension进程创建子进程;进程间通信与网络访问等。因此未遵从此约定可能会导致功能异常。
363
364## 相关实例
365
366针对InputMethodExtensionAbility开发,有以下相关实例可供参考:
367
368- [轻量级输入法](https://gitcode.com/openharmony/applications_app_samples/tree/master/code/Solutions/InputMethod/KikaInput)
369