更新记录
1.0.0(2025-03-03)
- 自定义输入法界面
- 输入法与宿主app通讯
- 实现输入法插入文本、回删、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 |
---|---|---|---|---|---|---|---|---|
× | × | × | × | × | × | × | × | × |
类似搜狗输入法自定义输入法键盘
优势:
- 自定义输入法界面
- 输入法与宿主app通讯
- 实现输入法插入文本、回删、return/换行、隐藏/切换键盘、移动光标等等功能
原理:
- 输入法采用webview加载h5,可以是本地或远程h5(本地H5需要支持file协议打开,对应uniapp项目设置“运行的基础路劲”为"./",H5的demo工程见示例项目里的static/KeyboardH5)
- 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
- 苹果官网上申请两个包名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签名文件
- 将输入法的签名文件文件名改为ios-com_cloud.mobileprovision,并替换到/nativeplugins/wrs-inputmethod/ios-com_cloud.mobileprovision
- 修改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
}
}
}
- 修改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
- 修改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设置
-
如果运行安装app后,没有运行app,直接在设置里面添加输入法,h5会加载默认地址
- ios默认加载nativeplugins/wrs-inputmethod/ios/Plugins/MyKeyboardExtension.appex/keyboard/index.html
- android默认加载app本地的nativeResources/android/assets/keyboard/index.html
-
当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)
}