• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# InputMethodExtensionAbility
2
3## When to Use
4[InputMethodExtensionAbility](../reference/apis/js-apis-inputmethod-extension-ability.md), inherited from [ExtensionAbility](extensionability-overview.md), is used for developing input method applications.
5
6The entire lifecycle of the [InputMethodExtensionAbility](../reference/apis/js-apis-inputmethod-extension-ability.md) instance and the owning ExtensionAbility process is scheduled and managed by the input method framework. The input method framework provides the [InputMethodExtensionAbility](../reference/apis/js-apis-inputmethod-extension-ability.md) base class. Derive this base class to implement initialization and resource clearing.
7
8[InputMethodExtensionAbility](../reference/apis/js-apis-inputmethod-extension-ability.md) provides related capabilities through [InputMethodExtensionContext](../reference/apis/js-apis-inputmethod-extension-context.md).
9
10
11## Implementing an Input Method Application
12
13InputMethodExtensionAbility provides the **onCreate()** and **onDestory()** callbacks, as described below. Override them as required.
14
15- **onCreate**
16  This callback is triggered when a service is created for the first time. You can perform initialization operations, for example, registering a common event listener.
17
18  > **NOTE**
19  >
20  > If a service has been created, starting it again does not trigger the **onCreate()** callback.
21
22- **onDestroy**
23  This callback is triggered when the service is no longer used and the instance is ready for destruction. You can clear resources in this callback, for example, deregister the listener.
24
25
26## How to Develop
27
28To implement an input method application, manually create an InputMethodExtensionAbility component in DevEco Studio. The procedure is as follows:
29
30In the **ets** directory of the target module, right-click and choose **New** > **Extension Ability** > **InputMethod** to a minimum template of InputMethodExtensionAbility.
31
32> **NOTE**
33>
34> When compiling the input method application, use the signature at the system_basic level. Otherwise, the application will not be able to start the keyboard.
35
36The minimum template implements an input method application with the most basic features, such as starting the keyboard, entering text, and deleting input. You can diversify the feature set of the application by, for example, adding the feature to hide the keyboard.
37
38The minimum template contains four files: **KeyboardController.ts**, **InputMethodService.ts**, **Index.ets**, and **KeyboardKeyData.ts**. The file directory is as follows:
39
40```
41/src/main/
42├── ets/inputmethodextability
43│   └──model/KeyboardController.ts			# Shows the keyboard.
44│   └──InputMethodService.ts				# Customizes a class that inherits from InputMethodExtensionAbility and add the required lifecycle callbacks.
45│   └──pages
46│      └── Index.ets						# Draws the keyboard and adds the input and deletion features.
47│      └── KeyboardKeyData.ts			    # Defines keyboard attributes.
48├── resources/base/profile/main_pages.json
49```
50
51## File Introduction
52
531. **InputMethodService.ts** file:
54
55   In the **InputMethodService.ts** file, add the dependency package for importing InputMethodExtensionAbility. Customize a class that inherits from InputMethodExtensionAbility and add the required lifecycle callbacks.
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();  // Initialize the window and register an event listener for the input method framework.
67     }
68
69     onDestroy() {
70       console.log("onDestroy.");
71       this.keyboardController.onDestroy(); // Destroy the window and deregister the event listener.
72     }
73   }
74   ```
75
762. **KeyboardController.ts** file:
77
78   ```ts
79   import inputMethodEngine from '@ohos.inputMethodEngine';
80   import display from '@ohos.display';
81   import windowManager from '@ohos.window';
82
83   // Call the getInputMethodAbility API to obtain an instance, and then call the other APIs of the input method framework based on the instance.
84   globalThis.inputAbility = inputMethodEngine.getInputMethodAbility();
85
86   export class KeyboardController {
87     mContext;	// Save the context attribute in InputMethodExtensionAbility.
88     WINDOW_TYPE_INPUT_METHOD_FLOAT = 2105;		// Define the window type. The value 2105 indicates the input method window type, which is used to create an input method application window.
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();				// Initialize the window.
102       this.registerListener();		// Register an event listener for the input method framework.
103     }
104
105     public onDestroy(): void			// Destroy the instance.
106     {
107       this.unRegisterListener();		// Deregister the event listener.
108       let win = windowManager.findWindow(this.windowName);
109       win.destroyWindow();				// Destroy the window.
110     }
111
112     private initWindow(): void		// Initialize the window.
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) => {	// Create a window of the specified type.
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();	// Register an event listener for the input method framework service.
141       globalThis.inputAbility.on('keyboardShow', () => {// Register an event listener for the keyboard .
142         if (this.isWindowShowing) {
143           return;
144         }
145         this.isWindowShowing = true;
146         this.showHighWindow(); // Show the window.
147       });
148       ...
149       // Register a listener for keyboard hiding.
150     }
151
152     private registerInputListener() {		// Register a listener for the enabling and disabling events of the input method framework service.
153       globalThis.inputAbility.on('inputStart', (kbController, textInputClient) => {
154         globalThis.textInputClient = textInputClient;		// This is an input method client instance, based on which you can call the functional APIs that the input method framework provides for the input method application.
155         globalThis.keyboardController = kbController;
156       })
157       globalThis.inputAbility.on('inputStop', (imeId) => {
158         if (imeId == "Bundle name/Ability name") {
159           this.mContext.destroy(); // Destroy the InputMethodExtensionAbility service.
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** file:
185
186   In this file you can define the content displayed on the soft keyboard.
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** file:
228
229   This file describes the functions of keys. For example, the number keys print numbers in the text box, and the delete key deletes what's entered.
230
231   Add the path to this file to the **src** field in the **resources/base/profile/main_pages.json** file.
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   // Component used for deletion.
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("Delete").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   // Numeric keyboard
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) => {  // First row on the numeric keyboard
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. Register the InputMethodExtensionAbility in the [module.json5 file](../quick-start/module-configuration-file.md) corresponding to the **Module** project. Set **type** to **"inputMethod"** and **srcEntry** to the code path of the InputMethodExtensionAbility component.
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