diff --git a/zh-cn/application-dev/application-models/inputmethodextentionability.md b/zh-cn/application-dev/application-models/inputmethodextentionability.md new file mode 100644 index 0000000000000000000000000000000000000000..9fa4a2b97e96b9d43d0d9d271663c3e5c9fdd07d --- /dev/null +++ b/zh-cn/application-dev/application-models/inputmethodextentionability.md @@ -0,0 +1,369 @@ +# InputMethodExtensionAbility开发指南 + +[InputMethodExtensionAbility](../reference/apis/js-apis-inputmethod-extension-ability.md)是inputMethod类型的ExtensionAbility组件,提供输入法框架服务相关扩展能力。 + +[InputMethodExtensionAbility](../reference/apis/js-apis-inputmethod-extension-ability.md)可以被其他组件启动或连接,并根据调用者的请求信息在后台处理相关事务。 + + +InputMethodExtensionAbility通过[InputMethodExtensionContext](../reference/apis/js-apis-inputmethod-extension-context.md)提供相关能力。 + + +## 实现一个输入法应用 + +[InputMethodExtensionAbility](../reference/apis/js-apis-inputmethod-extension-ability.md)提供了onCreate()和onDestory()生命周期回调,根据需要重写对应的回调方法。InputMethodExtensionAbility的生命周期如下: + +- **onCreate** + 服务被首次创建时触发该回调,开发者可以在此进行一些初始化的操作,例如注册公共事件监听等。 + + > **说明:** + > 如果服务已创建,再次启动该InputMethodExtensionAbility不会触发onCreate()回调。 + +- **onDestroy** + 当不再使用服务且准备将该实例销毁时,触发该回调。开发者可以在该回调中清理资源,如注销监听等。 + + +## 开发步骤 + +开发者在实现一个输入法应用时,需要在DevEco Studio工程中新建一个InputMethodExtensionAbility,具体步骤如下: + +在工程Module对应的ets目录下,右键选择“New > Extention Ability > InputMethod”,即可创建出InputMethodExtensionAbility的最小化模板。 + +> **说明:** +> 在编译输入法应用时,要使用system_core级别的签名,否则无法拉起输入法键盘。 +> [签名指导](https://developer.harmonyos.com/cn/docs/documentation/doc-guides/ohos-auto-configuring-signature-information-0000001271659465) + +最小化模板为一个最基本的输入法应用,包含软键盘拉起以及输入删除功能。后续开发者可在此基础上添加功能,如隐藏键盘等,实现自己的输入法应用。 + +最小化模板主要包含四个文件,分别为KeyboardController.ts、InputMethodService.ts、Index.ets以及KeyboardKeyData.ts。目录如下: + +``` +/src/main/ +├── ets/inputmethodextability +│ └──model/KeyboardController.ts # 显示键盘 +│ └──InputMethodService.ts # 自定义类继承InputMethodExtensionAbility并加上需要的生命周期回调 +│ └──pages +│ └── Index.ets # 绘制键盘,添加输入删除功能 +│ └── KeyboardKeyData.ts # 键盘属性定义 +├── resources/base/profile/main_pages.json +``` + +## 文件介绍 + +1. InputMethodService.ts文件。 + + 在InputMethodService.ts文件中,增加导入InputMethodExtensionAbility的依赖包,自定义类继承InputMethodExtensionAbility并加上需要的生命周期回调。 + + ```ts + import InputMethodExtensionAbility from '@ohos.InputMethodExtensionAbility'; + import { KeyboardController } from './model/KeyboardController' + + export default class InputDemoService extends InputMethodExtensionAbility { + private keyboardController: KeyboardController; + + onCreate(want) { + this.keyboardController = new KeyboardController(this.context); + this.keyboardController.onCreate(); // 初始化窗口并注册对输入法框架的事件监听 + } + + onDestroy() { + console.log("onDestroy."); + this.context.destroy(); + } + } + ``` + +2. KeyboardController.ts文件。 + + ```ts + import inputMethodEngine from '@ohos.inputMethodEngine'; + import display from '@ohos.display'; + import windowManager from '@ohos.window'; + + // 调用输入法框架的getInputMethodAbility方法获取实例,并由此实例调用输入法框架功能接口 + globalThis.inputAbility = inputMethodEngine.getInputMethodAbility(); + + export class KeyboardController { + mContext; // 保存InputMethodExtensionAbility中的context属性 + WINDOW_TYPE_INPUT_METHOD_FLOAT = 2105; // 定义窗口类型,2105代表输入法窗口类型,用于创建输入法应用窗口 + windowName = 'inputApp'; + private windowHeight: number = 0; + private windowWidth: number = 0; + private nonBarPosition: number = 0; + private isWindowShowing: boolean = false; + + constructor(context) { + this.mContext = context; + } + + public onCreate(): void + { + this.initWindow(); // 初始化窗口 + this.registerListener(); // 注册对输入法框架的事件监听 + } + + public onDestroy(): void // 应用生命周期销毁 + { + this.unRegisterListener(); // 注销事件监听 + let win = windowManager.findWindow(this.windowName); + win.destroyWindow(); // 销毁窗口 + this.mContext.terminateSelf(); // 销毁InputMethodExtensionAbility服务 + } + + private initWindow(): void // 初始化窗口 + { + let dis = display.getDefaultDisplaySync(); + let dWidth = dis.width; + let dHeight = dis.height; + let keyHeightRate = 0.47; + let keyHeight = dHeight * keyHeightRate; + this.windowWidth = dWidth; + this.windowHeight = keyHeight; + this.nonBarPosition = dHeight - keyHeight; + + let config = { + name: this.windowName, + windowType: this.WINDOW_TYPE_INPUT_METHOD_FLOAT, + ctx: this.mContext + } + windowManager.createWindow(config).then((win) => { // 根据窗口类型创建窗口 + win.resize(dWidth, keyHeight).then(() => { + win.moveWindowTo(0, this.nonBarPosition).then(() => { + win.setUIContent('pages/InputMethodExtAbility/Index').then(() => { + }); + }); + }); + }); + } + + private registerListener(): void + { + this.registerInputListener(); // 注册对输入法框架服务的监听 + globalThis.inputAbility.on('keyboardShow', () => { // 注册显示键盘事件监听 + if (this.isWindowShowing) { + return; + } + this.isWindowShowing = true; + this.showHighWindow(); // 显示窗口 + }); + ... + // 注册隐藏键盘事件监听等 + } + + private registerInputListener() { // 注册对输入法框架服务的开启及停止事件监听 + globalThis.inputAbility.on('inputStart', (kbController, textInputClient) => { + globalThis.textInputClient = textInputClient; // 此为输入法客户端实例,由此调用输入法框架提供给输入法应用的功能接口 + globalThis.keyboardController = kbController; + }) + globalThis.inputAbility.on('inputStop', (imeId) => { + if (imeId == "包名/Ability名") { + this.onDestroy(); + } + }); + } + + private unRegisterListener(): void + { + globalThis.inputAbility.off('inputStart'); + globalThis.inputAbility.off('inputStop', () => {}); + globalThis.inputAbility.off('keyboardShow'); + } + + private showHighWindow() { + let win = windowManager.findWindow(this.windowName) + win.resize(this.windowWidth, this.windowHeight).then(() => { + win.moveWindowTo(0, this.nonBarPosition).then(() => { + win.showWindow().then(() => { + this.isWindowShowing = false; + }) + }) + }) + } + } + ``` + +3. KeyboardKeyData.ts文件。 + + 定义软键盘的按键显示内容。 + + ```ts + export interface sourceListType { + content: string, + } + + export let numberSourceListData: sourceListType[] = [ + { + content: '1' + }, + { + content: '2' + }, + { + content: '3' + }, + { + content: '4' + }, + { + content: '5' + }, + { + content: '6' + }, + { + content: '7' + }, + { + content: '8' + }, + { + content: '9' + }, + { + content: '0' + } + ] + ``` + +4. Index.ets文件。 + + 主要描绘了具体按键功能。如按下数字键,就会将数字内容在输入框中打印出来,按下删除键,就会将内容删除。 + + 同时在resources/base/profile/main_pages.json文件的src字段中添加此文件路径。 + + ```ets + import { numberSourceListData, sourceListType } from './keyboardKeyData' + + @Component + struct keyItem { + private keyValue: sourceListType + @State keyBgc: string = "#fff" + @State keyFontColor: string = "#000" + + build() { + Column() { + Flex({ direction: FlexDirection.Column, + alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { + Text(this.keyValue.content).fontSize(20).fontColor(this.keyFontColor) + } + } + .backgroundColor(this.keyBgc) + .borderRadius(6) + .width("8%") + .height("65%") + .onTouch((event: TouchEvent) => { + if (event.type === TouchType.Down) { + globalThis.textInputClient.insertText(this.keyValue.content); + } + }) + } + } + + // 删除组件 + @Component + export struct deleteItem { + @State keyBgc: string = "#fff" + @State keyFontColor: string = "#000" + + build() { + Column() { + Flex({ direction: FlexDirection.Column, + alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { + Text("删除").fontSize(20).fontColor(this.keyFontColor) + } + } + .backgroundColor(this.keyBgc) + .width("13%") + .borderRadius(6) + .onTouch((event: TouchEvent) => { + if (event.type === TouchType.Down) { + globalThis.textInputClient.deleteForward(1); + } + }) + } + } + + // 数字键盘 + @Component + struct numberMenu { + private numberList: sourceListType[] + + build() { + Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.SpaceEvenly }) { + Flex({ justifyContent: FlexAlign.SpaceBetween }) { + ForEach(this.numberList, (item: sourceListType) => { // 数字键盘第一行 + keyItem({ keyValue: item }) + }, (item: sourceListType) => item.content); + } + .padding({ top: "2%" }) + .width("96%") + .height("25%") + + Flex({ justifyContent: FlexAlign.SpaceBetween }) { + deleteItem() + } + .width("96%") + .height("25%") + } + } + } + + @Entry + @Component + struct Index { + private numberList: sourceListType[] = numberSourceListData + + build() { + Stack() { + Flex({ + direction: FlexDirection.Column, + alignItems: ItemAlign.Center, + justifyContent: FlexAlign.End + }) { + Flex({ + direction: FlexDirection.Column, + alignItems: ItemAlign.Center, + justifyContent: FlexAlign.SpaceBetween + }) { + numberMenu({ + numberList: this.numberList + }) + } + .align(Alignment.End) + .width("100%") + .height("75%") + } + .height("100%").align(Alignment.End).backgroundColor("#cdd0d7") + } + .position({ x: 0, y: 0 }).zIndex(99999) + } + } + ``` + +5. 在工程Module对应的[module.json5配置文件](../quick-start/module-configuration-file.md)中注册InputMethodExtensionAbility,type标签需要设置为“inputMethod”,srcEntrance标签表示当前InputMethodExtensionAbility组件所对应的代码路径。 + + ```ts + { + "module": { + // ... + "extensionAbilities": [ + { + "description": "输入法Demo", + "icon": "$media:icon", + "name": "InputDemoService", + "srcEntrance": "./ets/InputMethodExtAbility/InputMethodService.ts", + "type": "inputMethod", + "visible": true, + } + ] + } + } + ``` + + + + +## 相关示例 + +针对InputMethodExtensionAbility开发,有以下相关示例可供参考: + +[Kika输入法](https://gitee.com/openharmony/applications_app_samples/tree/master/CompleteApps/KikaInput) \ No newline at end of file