更新记录

1.0.0(2025-03-03)

  1. 自定义输入法界面
  2. 输入法与宿主app通讯
  3. 实现输入法插入文本、回删、return/换行、隐藏/切换键盘、移动光标等等功能

平台兼容性

Vue2 Vue3
App 快应用 微信小程序 支付宝小程序 百度小程序 字节小程序 QQ小程序
HBuilderX 3.6.8,Android:4.4,iOS:9,HarmonyNext:不支持 × × × × × ×
钉钉小程序 快手小程序 飞书小程序 京东小程序 鸿蒙元服务
× × × × ×
H5-Safari Android Browser 微信浏览器(Android) QQ浏览器(Android) Chrome IE Edge Firefox PC-Safari
× × × × × × × × ×

类似搜狗输入法自定义输入法键盘

优势:

  1. 自定义输入法界面
  2. 输入法与宿主app通讯
  3. 实现输入法插入文本、回删、return/换行、隐藏/切换键盘、移动光标等等功能

原理:

  1. 输入法采用webview加载h5,可以是本地或远程h5(本地H5需要支持file协议打开,对应uniapp项目设置“运行的基础路劲”为"./",H5的demo工程见示例项目里的static/KeyboardH5)
  2. h5与app之间可以相互保存获取数据

集成步骤:

  • 下载demo示例功能,HBuilderX导入的时候选择vue3(vue2和vue3都支持)
  • 拷贝demo里的nativeplugins/wrs-inputmethod、nativeResources、AndroidManifest.xml到项目对应到目录
  • 在manifest.json的原生插件配置-》本地插件里勾选WRSInputmethod插件
  • 参考https://www.cnblogs.com/wenrisheng/p/18323027集成本插件到项目里

ios

  1. 苹果官网上申请两个包名identify,一个给app,一个给输入法extension,输入法的包名以app包名位前缀(${包名}.${自定义后缀}),如:
复制代码
app包名:com.onecloud.powerone.HelloProject
则输入法的包名:com.onecloud.powerone.HelloProject.MyKeyboard

点击分别进入官网上的app和输入法信息页面,勾选Capabilities -> App Groups,并增加一个相同的group Identifier,app和输入法的group Identifier一定要一样一样一样!!,然后再生成app和输入法的.mobileprovision签名文件

  1. 将输入法的签名文件文件名改为ios-com_cloud.mobileprovision,并替换到/nativeplugins/wrs-inputmethod/ios-com_cloud.mobileprovision
  2. 修改nativeplugins/wrs-inputmethod/ios-extension.json文件:
复制代码
{
    "MyKeyboardExtension.appex": {
        "identifier": "com.onecloud.powerone.HelloProject.MyKeyboard", // identifier设置为输入法的包名
        "profile": "ios-com_cloud.mobileprovision",
        "plists": {
            "UTSKeyboardGroup": "group.com.wrs.keyboard", // UTSKeyboardGroup设置为输入法的group Identifier
            "CFBundleDisplayName": "uniapp输入法", // CFBundleDisplayName设置输入法名称,当输入法名称与app名称一样时,显示为app名称,否则显示为${输入法名称}-{app名称}
            "NSExtensionAttributes": {
                "IsASCIICapable": true,
                "PrefersRightToLeft": false,
                "PrimaryLanguage": "zh", // 中文
                "RequestsOpenAccess": true // 是否请求输入法的“允许完全访问”权限
            }
        },
        "entitlements": {
            "com.apple.security.application-groups": ["group.com.wrs.keyboard"] // com.apple.security.application-groups数组里增加输入法的group Identifier
        }
    }
}
  1. 修改nativeResources/ios/UniApp.entitlements文件:
复制代码
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>com.apple.security.application-groups</key>
    <array>
        <string>group.com.wrs.keyboard</string> //这个改为app的group Identifier
    </array>
</dict>
</plist>

android

  1. 修改nativeResources/android/res/xml/method.xml文件:
复制代码
<?xml version="1.0" encoding="utf-8"?>
<input-method xmlns:android="http://schemas.android.com/apk/res/android"
    android:label="uniapp输入法" android:settingsActivity="io.dcloud.PandoraEntry"
    />

 <!--label:设置输入法的名称,settingsActivity:设置在系统输入法设置界面,也可以不设置,点击该输入法的设置按钮时需要跳转的页面,一般设置为本app的启动activity,uniapp默认的是io.dcloud.uniapp.UniAppActivity-->

做完以上步骤,一定要删除本地基座和手机上的app,重新自定义基座运行

输入法键盘h5的url设置

  1. 如果运行安装app后,没有运行app,直接在设置里面添加输入法,h5会加载默认地址

    • ios默认加载nativeplugins/wrs-inputmethod/ios/Plugins/MyKeyboardExtension.appex/keyboard/index.html
    • android默认加载app本地的nativeResources/android/assets/keyboard/index.html
  2. 当app启动后,可以调用UTSInputMethod.setH5Url接口设置(一般用于开发测试使用),支持本地和远程H5,也可以在输入法的h5里调用setH5Url接口设置

接口

接口氛围2个模块,一个是给app调用的,一个是给H5里调用的,两边通过共享物理存储来通讯

app调用的接口

复制代码
import {
    UTSInputMethod,
    UTSFileUtils
} from "@/uni_modules/wrs-uts-inputmethod"
复制代码
let h5Url = ""
switch (uni.getSystemInfoSync().platform) {
    // android
    case 'android': {
        let srcPath = plus.io.convertLocalFileSystemURL("_www/static/keyboard")
        h5Url = srcPath + "/index.html"
    }
    break;
    case "ios": {
        // ios加载本地H5时,需要把h5拷贝到group里,输入法只能访问group里的文件,不能访问app里的文件
        let groupPath = UTSInputMethod.getGroupPath(this.group)
        let srcPath = plus.io.convertLocalFileSystemURL("_www/static/keyboard")
        let destPath = groupPath + "/keyboard"
        h5Url = destPath + "/index.html"
        let isExist = UTSFileUtils.realIsFileExists(h5Url)
        if (!isExist) {
            // 如果group不存在文件,则把h5文件拷贝到group里
            UTSFileUtils.copy(srcPath, destPath, (suc) => {
                // UTSFileUtils.listFilesInDir(destPath, (resp) => {
                //  this.showMsg("path:" + destPath + "\n" + JSON.stringify(resp))
                // })
            })
        } else {
            this.showMsg("h5已经存在")
        }

    }
    break;
    default:
        break;
}

// h5Url = "http://192.168.1.41:8080/"

let params = {}
params.group = this.group // group参数仅iOS需要,android不需要
params.url = h5Url // url可以是本地的h5文件,也可以是在线链接,当url是本地h5时,在iOS里需要把h5拷贝到group的共享目录下才能被输入法访问到
UTSInputMethod.setH5Url(params, (resp)=>{

})
  • 保存key-value数据
复制代码
let key = "name";
let value = "app数据"
let params = {}
params.group = this.group // group仅支持iOS,iOS的数据一定要保存到group里面,这样输入法的h5里才能获取到数据
params.key = key
params.value = value
UTSInputMethod.setData(params, (resp)=>{})
  • 获取key-value数据
复制代码
let key = "name";
let params = {}
params.group = this.group
params.key = key
UTSInputMethod.getData(params, (resp)=>{
    let value = resp.value
    this.showMsg("getData:" + JSON.stringify(resp))
})
  • 删除key-value数据
复制代码
let key = "name";
let params = {}
params.group = this.group 
params.key = key
UTSInputMethod.removeData(params, (resp)=>{

})
  • 保存为文件,仅支持iOS
复制代码
let path = "bb/config.txt"; // ios的path路径不能以/开头,android的path以/开头的绝对路径,下面接口类似
let value = "app数据"
let params = {}
params.path = path
params.group = this.group // group仅支持iOS,iOS的数据一定要保存到group里面,这样输入法的h5里才能获取到数据
params.value = value
UTSInputMethod.setFileData(params, (resp)=>{
    this.showMsg("setFileData:" + JSON.stringify(resp))
})
  • 删除文件,仅支持iOS
复制代码
let path = "config.txt";
let params = {}
params.path = path
params.group = this.group
UTSInputMethod.removeFileData(params, (resp)=>{
    this.showMsg("removeFileData:" + JSON.stringify(resp))
})
  • 获取文件内容,仅支持iOS
复制代码
let path = "config.txt";
let params = {}
params.path = path
params.group = this.group
UTSInputMethod.getFileData(params, (resp)=>{
    this.showMsg("getFileData:" + JSON.stringify(resp))
})

输入法h5调用的接口

接口参考demo里static/KeyboardH5工程

  • 收起键盘
复制代码
// 隐藏键盘
function hideKeyboard() {
    var params = {};
    params.opt = "hideKeyboard"
    callNative(params)
}
  • 切换输入法
复制代码
//  iOS切换到下一输入法,android弹出输入法选择框
function nextInputMode() {
    var params = {};
    params.opt = "nextInputMode"
    callNative(params)
  • 给输入框插入文本
复制代码// 往输入框插入文本
function insertText(txt) {
    var params = {};
    params.opt = "insertText"
    params.txt = "asdfasfd"
    callNative(params)
}
  • 设置键盘高度
复制代码
// 更新输入键盘高度
function setKeyboardHeight(height) {
    var params = {};
    params.opt = "setKeyboardHeight"
    params.height = height
    callNative(params)
}
  • 回删
复制代码// 向前删除文字
function deleteBackward() {
    var params = {};
    params.opt = "deleteBackward"
    callNative(params)
}
  • 获取输入框信息,比如输入框的键盘类型、是否开启"允许完全访问权限"权限
复制代码
getTextInfo((resp) => {
    if(IOS) { // ios
        // 输入法的"允许完全访问权限"权限是否已经开启
        let hasFullAccess = resp.hasFullAccess
        // 输入框文本类型,一般用来判断是显示数字键盘还是字母键盘或者其他键盘
        let keyboardType = resp.keyboardType
        switch (keyboardType) {
            case 0: {
                // default
            }
            break;
            case 1: {
                // asciiCapable
            }
            break;
            case 2: {
                // numbersAndPunctuation
            }
            break;
            case 3: {
                // URL
            }
            break;
            case 4: {
                // numberPad
            }
            break;
            case 5: {
                // phonePad
            }
            break;
            case 6: {
                // namePhonePad
            }
            break;
            case 7: {
                // emailAddress
            }
            break;
            case 8: {
                // decimalPad
            }
            break;
            case 9: {
                // twitter
            }
            break;
            case 10: {
                // webSearch
            }
            break;
            case 11: {
                // asciiCapableNumberPad
            }
            break;
            default:
                break;
        }
    } else { // android
    switch (keyboardType) {
    case 1: {
        // TYPE_CLASS_TEXT

    }
    break;
    case 2: {
        // TYPE_CLASS_NUMBER
    }
    break;
    case 3: {
        // TYPE_CLASS_PHONE
    }
    break;
    case 4: {
        // TYPE_CLASS_DATETIME
    }
    break;
    default:
        break;
}
    }

    this.showMsg("getTextDocument:" + JSON.stringify(resp))
})

function getTextInfo(callback) {
    var callBackFunName = getJSCallBackFunName();
    // 原生只能回调全局的函数,局部函数无法调用
    window[callBackFunName] = function(resp) {
        // 释放回调函数
        window[callBackFunName] = null;
        callback(resp);
    };
    var params = {};
    params.opt = "getTextInfo"
    params.callback = callBackFunName
    callNative(params)
}
  • 获取key-value数据
复制代码// getData, 一般用于app与键盘直接共享配置信息等数据,用于保存小量的key-value数据,大数据请用setFileData来保存
function getData(key, callback){
    var callBackFunName = getJSCallBackFunName();
    // 原生只能回调全局的函数,局部函数无法调用
    window[callBackFunName] = function(resp) {
        // 释放回调函数
        window[callBackFunName] = null;
        callback(resp);
    };
    var params = {};
    params.opt = "getData"
    params.key = key
    params.callback = callBackFunName
    callNative(params)
}
  • 设置key-value数据
复制代码
// value最好是字符串或者数字类型,其他类型不一定支持
function setData(key, value){
    var params = {};
    params.opt = "setData"
    params.key = key
    params.value = value
    callNative(params)
}
  • 删除key-value数据
复制代码
function removeData(key){
    var params = {};
    params.opt = "removeData"
    params.key = key
    callNative(params)
}
  • 读取文件内容,仅支持iOS
复制代码
function getFileData(path, callback) {
    var params = {};
    params.opt = "getFileData"
    params.path = path
    var callBackFunName = getJSCallBackFunName();
    // 原生只能回调全局的函数,局部函数无法调用
    window[callBackFunName] = function(resp) {
        // 释放回调函数
        window[callBackFunName] = null;
        callback(resp);
    };
    params.callback = callBackFunName
    callNative(params)
}

隐私、权限声明

1. 本插件需要申请的系统权限列表:

android: <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC"/> ios: 无

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

插件不采集任何数据

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率:

使用中有什么不明白的地方,就向插件作者提问吧~ 我要提问