• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.tsInputMethodService.tsIndex.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
55InputMethodService.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